[
  {
    "path": ".gitattributes",
    "content": "# Normalize EOL for all files that Git considers text files.\n* text=auto eol=lf\n\n# Only include the addons/guide folder when downloading from the Asset Library.\n/**        export-ignore\n/addons    !export-ignore\n/addons/guide !export-ignore\n/addons/guide/** !export-ignore\n# also include the examples folder\n/guide_examples !export-ignore\n/guide_examples/** !export-ignore\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: derkork\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\notechie: # Replace with a single Otechie username\nlfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry\ncustom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug-.md",
    "content": "---\nname: 'Bug '\nabout: I have encountered a bug\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\nGodot Version (_e.g. 4.2, 4.3, 4.4beta3_): \nG.U.I.D.E Version (_e.g. 0.3.1_):\n\n**The bug**\n\nPlease include the following information as it really helps getting problems solved quicker:\n- What were you trying to achieve?\n- What happened?\n- What would you have expected instead?\n- Screenshots and/or minimal examples to reproduce the issue.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/question.md",
    "content": "---\nname: Question\nabout: I have a question on how to use the library.\ntitle: ''\nlabels: knowledge\nassignees: ''\n\n---\n\nWrite your question here. Try to be specific (e.g. \"how can I detect from which controller an input comes\" is a lot better than \"how do i make a  local multiplayer game with this\").\n"
  },
  {
    "path": ".gitignore",
    "content": "# Godot 4+ specific ignores\n.godot/\nandroid/\naddons/explore-editor-theme/\n\n# IntelliJ IDEA\n.idea/\n\n# macOS\n.DS_Store\n\nzz-sup/\n\nreports/\nprogress.md\ntask_plan.md\nfindings.md\n.ggg.state\n"
  },
  {
    "path": ".junie/guidelines.md",
    "content": "﻿\n## Generic code guidelines\nAlways use tabs for indentation. Always add GDScript type hints everywhere. When doing null checks use `is_instance_valid(object)` rather than `object != null`. GDScripts should always end with an empty line. All `@onready` variables are private and need to start with an underscore. For constructs, that refer to other code, always use the type-safe variant without strings, e.g:\n\n- `my_signal.emit()` rather than `emit_signal(\"my_signal\")`\n- `foo.some_signal.connect(_on_foo_some_signal)` rather than `foo.connect(\"some_signal\", self, \"_on_foo_some_signal\")`\n\nOmit `self` when not stricly needed. GDScript does not support nested named functions. So something like:\n\n```gdscript\nfunc foo():\n    func bar():\n        print(\"Hello world!\")\n    bar()\n```\n\ndoes not work. If needed, you can make a lambda:\n\n```gdscript\nfunc foo():\n    var bar = func(): print(\"Hello world!\")\n    bar()\n    \n```\n\nLambdas should be used sparingly, e.g., in functions that take callables as arguments. For example, `Array.sort_custom()`.\n  \n\n## Accessing nodes \nWhen referring to nodes, always use scene unique names instead of paths. If a node has no scene unique name in a scene, but you need it in a script, then add a scene unique name. Always initialize node references in an `@onready` variable rather than littering the code with inline node references. When getting a node from the scene use the provided `Assure.exists` method to check that the node is there and of the right type (e.g. `@onready var _my_node: Node2D = Assure.exists(%MyNode as Node2D)`). This will ensure that the node exists and is of the correct type, and will throw an error if it is not. This also means that we don't need to check for null or type errors later in the code.\n\n## Documentation\nAlways document your code using the GDScript documentation style. Use `@param` to document parameters, `@return` to document return values, and `@signal` to document signals. Documentation should focus on the \"why\" rather than the \"how\". Don't document the obvious, but rather explain the reasoning behind the code. Write in a simple, clear style avoid fancy language. Documentation for classes always goes in front of the `class_name` line, documentation for methods goes in front of the method definition. Example:\n\n```gdscript\n## This class provides utility functions for the project.\nclass_name Utilties\n\n## Divides an integer number by another integer. \n## @param a The first integer to divide.\n## @param b The second integer to divide. This must not be zero.\n## @return The result of the division.\nstatic func divide(a: int, b: int) -> int:\n    assert(b != 0, \"Division by zero is not allowed.\")\n    return a / b\n```\n\nAll documentation happens in the code, don't add any extra documentation files.\n\n## UIDs\nNever mess with UIDs. They are automatically generated by Godot and should not be changed manually. Use paths to refer to resources, Godot will automatically convert this to UIDs when needed. If you need to refer to a resource in code, use the `load()` function with the path to the resource. Also never generate .uid files manually, they are automatically generated by Godot when the resource is saved. If you refer to resources from other resources always use the path not the uid. Leave out the uid attribute in the resource file, it will be automatically generated by Godot when the resource is saved.\n\n## Writing tests\nThe test framework does not stop execution on failed assertions, so make sure the code after an assertion doesn't assume that the assertion was successful.\n\n## Running tests\nYou can run tests using the `.\\run_tests.cmd` file in the project root. This will run GDUnit4 in Godot and print the test results. Only run tests when specifically being asked about it.\n\n- To run only a single test suite, you can use the `-a` parameter to specify the test file or method. For example:\n\n    ```\n    .\\run_tests.cmd -a tests\\test_large_trigger_plate\\test_large_trigger_plate.gd\n    ```\n\n- To run a specific test method, use the format `test_file.gd:test_method_name`:\n\n    ```\n    .\\run_tests.cmd -a tests\\test_large_trigger_plate\\test_large_trigger_plate.gd:test_trigger_reacts_to_two_bodies\n    ```"
  },
  {
    "path": "CHANGES.md",
    "content": "# Changelog\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n### Added\n- `GUIDEInputFormatter` now has a `for_contexts` factory method which provides a formatter that works on the given contexts. This is useful if you need information from multiple contexts without these contexts currently being active ([#190](https://github.com/godotneers/G.U.I.D.E/issues/190)).\n\n### Improved\n- The mapping context editor now has menu options to insert action mappings and input mappings before and after the current item. This makes it easier to work with larger mapping contexts ([#190](https://github.com/godotneers/G.U.I.D.E/issues/190))\n\n### Fixed\n- Fixed a bug where the _Any_ input would keep firing continuously after the application loses focus (e.g. by pressing Alt+Tab or the Windows key). This happened because Godot clears its own input state directly without dispatching input events when the application loses focus, causing G.U.I.D.E's internal input state to diverge ([#189](https://github.com/godotneers/G.U.I.D.E/issues/189)).\n\n\n## [0.12.0] - 2026-03-27\n### Improved\n- The direction icons for all built-in input renderers (controller, mouse, touch, joy) have been redesigned. Instead of showing the device icon and the direction arrow side by side, the direction arrow is now overlaid on top of a slightly inset device icon, making input prompts more visually compact. A big thanks goes to [SilverWolveGames](https://github.com/SilverWolveGames) for contributing the new icons and the initial implementation ([#179](https://github.com/godotneers/G.U.I.D.E/issues/179),[#182](https://github.com/godotneers/G.U.I.D.E/pull/182))!\n\n### Fixed\n- Fixed a bug where the input formatter would not properly format chorded inputs when input filtering was active ([#175](https://github.com/godotneers/G.U.I.D.E/issues/175)).\n\n\n## [0.12.0-beta1] - 2026-02-23\n### Added\n- G.U.I.D.E now supports merging of multiple mapping contexts with overlapping actions. This allows you to define multiple mapping contexts that have different inputs bound to the same action. Upon merge, these will be combined into a single action definition. This is useful for supporting multiple different input methods at the same time. Before, only one definition of an action could be active at a time and loading a second one would replace the first one. Because this is quite complex to get right under the hood, this feature is currently only available in beta releases on Github and will only be published to the asset library once there is enough feedback from real-world usage. There are no breaking API changes for this feature, so it should drop right into your project without any changes required. A huge thanks goes out to [ShadowCommander](https://github.com/ShadowCommander) for contributing this feature and helping so much with the debugging ([#168](https://github.com/godotneers/G.U.I.D.E/pull/168))!\n- There is now a new method which allows you to quickly replace all existing mapping contexts with a new set. This is often needed in UI dialogues and until now required keeping track of what mapping contexts were active before replacing them. Now you can call `var old_mapping_contexts := GUIDE.set_enabled_mapping_contexts(new_mapping_contexts)`. This will swap the current set of enabled mapping contexts with the new set and return the old set.\n\n## [0.11.2] - 2026-02-17\n### Fixed\n- The _Any_ input now properly fires continuously when used with a _Down_ trigger. Previously it would only fire once on the initial press rather than every frame while the input was held ([#171](https://github.com/godotneers/G.U.I.D.E/issues/171)).\n\n## [0.11.1] - 2026-02-10\n### Fixed\n- Fixed a runtime error that could occur when disconnecting and reconnecting controllers during gameplay ([#164](https://github.com/godotneers/G.U.I.D.E/issues/164), [#169](https://github.com/godotneers/G.U.I.D.E/pull/169)). A big thanks goes out to [Phlegmlee](https://github.com/Phlegmlee) for contributing a PR for this!\n- Fixed a regression where the input tracker would not work correctly when the game was paused.\n\n## [0.11.0] - 2026-02-06\n### Added\n- Added a new hair trigger. Hair triggers reduce the travel distance needed to register a full trigger press, enabling faster response times. Useful for quick actions like rapid firing or accelerating in racing games. A huge thanks goes out to [ShadowCommander](https://github.com/ShadowCommander) who contributed this feature ([#24](https://github.com/godotneers/G.U.I.D.E/issues/24), [#167](https://github.com/godotneers/G.U.I.D.E/pull/167))!\n\n\n## [0.10.1] - 2026-02-02\n### Fixed\n- The input tracker now handles some edge cases better on UI components that are overridden by the game ([#166](https://github.com/godotneers/G.U.I.D.E/issues/166)) \n\n## [0.10.0] - 2026-01-16\n### Breaking Changes\n- The _Released_ trigger now makes the action go into _Ongoing_ state while the input is actuated. Previously it would stay in _Completed_. This is useful for touch input to be able to acquire touch position information while the input is still active ([#144](https://github.com/godotneers/G.U.I.D.E/issues/144)). This change should not affect most users, but since it changes the behavior of this trigger, I still declare this a breaking change.\n\n### Added\n- The Steam Deck controller is now supported as its own controller type ([#152](https://github.com/godotneers/G.U.I.D.E/issues/152)). A big thanks goes to [Live, Laugh, Luke](https://github.com/live-laugh-luke) for helping to get this on the road and testing it on their Steam Deck!\n\n### Improved\n- The [trigger reference page](https://godotneers.github.io/G.U.I.D.E/reference/triggers) now contains some state diagrams showing the behavior of each trigger type. This makes it easier to understand how each trigger behaves.\n\n## [0.9.1] - 2026-01-11\n### Fixed\n- The icons for right stick and left stick click are no longer mixed up for Microsoft-style controllers ([#159](https://github.com/godotneers/G.U.I.D.E/issues/159))\n\n## [0.9.0] - 2026-01-03\n### Added\n- `GUIDEAction` now has a new signal `just_triggered` that is emitted the first frame that the action is triggered. A huge thanks goes to [Josh Taylor](https://github.com/codeinclined) for submitting a PR for this ([#155](https://github.com/godotneers/G.U.I.D.E/pull/155)).\n\n## [0.9.0-beta3] - 2025-12-13\n### Fixed\n- `GUIDEInputDetector` now again properly emits the `detection_started` signal ([#142](https://github.com/godotneers/G.U.I.D.E/issues/142)).\n- Inputs are now properly formatted in exported games again ([#149](https://github.com/godotneers/G.U.I.D.E/issues/149))\n\n## [0.9.0-beta2] - 2025-11-30\n### Breaking Changes\n- The input formatting system has received an overhaul to make it more flexible. This has been done in a way that is backwards compatible, as long as the game code only uses `GUIDEInputFormatter` to interface with the formatting system. If you have written a custom icon renderer or text provider, you will need to do a few slight adjustments to your code:\n  - The functions `GUIDEIconRenderer.supports`, `GUIDEIconRenderer.render` and `GUIDEIconRenderer.cache_key` now take an additional parameter of type `GUIDEInputFormattingOptions`. This parameter contains the newly introduced formatting options that are currently active on the `GUIDEInputFormatter`. Unless you want to make use of these, all you need to do is to add this new parameter to the function signatures.\n  - Similarly `GUIDETextProvider.supports` and `GUIDETextProvider.get_text` now take an additional parameter of type `GUIDEInputFormattingOptions`.\n\n### Added\n- It is now possible to create render styles for the built-in icon renderers. This allows you to customize the icons used in input prompts to match the style of your game without having to write a custom renderer. See the [documentation](https://godotneers.github.io/G.U.I.D.E/usage/input-prompts#customizing-the-style-of-the-input-prompt-icons) for more information.\n- It is now [possible to force a specific set of icons](https://godotneers.github.io/G.U.I.D.E/usage/input-prompts#controlling-the-displayed-controller-icons) to display for controllers, no matter which type of controller is currently connected. This allows you to work around issues with generic controllers not being detected correctly or just to cater for player who want to select which icons they see ([#133](https://github.com/godotneers/G.U.I.D.E/issues/133)). \n- This feature allows you to force the display of specific controller icons in the editor. There is a new project setting for this. Enable _Advanced_ settings and check the _GUIDE/Editor_ section of the project settings to enable this ([#132](https://github.com/godotneers/G.U.I.D.E/discussions/132)).\n- It is now possible to [only show input related to certain device types](https://godotneers.github.io/G.U.I.D.E/usage/input-prompts#controlling-the-display-of-mixed-inputs) in the input prompts. This is useful in simple scenarios where you just want to bind multiple different inputs to the same action and don't want to to have input prompts like \"press Space, left mouse button or RB to jump\" ([#139](https://github.com/godotneers/G.U.I.D.E/issues/139)).\n- It is now possible to quickly select a mapping context directly from the mapping context editor without having to search the tree for it ([#124](https://github.com/godotneers/G.U.I.D.E/issues/124)).\n\n### Fixed\n- The `GUIDEModifier3DCoordinates` now also works when physics are processed in a separate thread ([#91](https://github.com/godotneers/G.U.I.D.E/issues/91)). A big thanks goes to [elsen0xcc](https://github.com/elsen0xcc) for submitting a PR for this ([#138](https://github.com/godotneers/G.U.I.D.E/pull/138)).\n- Type hints are now added to all files, allowing to run the project in strict typing mode ([#130](https://github.com/godotneers/G.U.I.D.E/issues/130)).\n\n## [0.9.0-beta1] - 2025-11-04\n\n### Added\n- G.U.I.D.E has now support for virtual on-screen joysticks and buttons. These work both with mouse and touch inputs. This is useful for mobile games and allows steering controls just the touch screen. There is a new demo in the `guide_examples` folder showing how to use this. You can also check the [documentation](https://godotneers.github.io/G.U.I.D.E/usage/virtual-joysticks) for more information on how to use them. \n- `GUIDEMappingContext` now has signals `enabled` and `disabled` which fire when the context is enabled or disabled. This can be useful to trigger some game logic when a context is activated or deactivated. A big thanks goes to [Jonathan Durbin](https://github.com/jonathan-durbin) for providing a PR for this ([#119](https://github.com/godotneers/G.U.I.D.E/pull/119)).\n\n- The virtual cursor modifier now has a setting allowing to synchronize the cursor position with the actual mouse position when activated or deactivated. This is useful when switching between a gamepad and a mouse to avoid jumps in the cursor position. The virtual cursor demo has been updated to make use of this functionality ([#122](https://github.com/godotneers/G.U.I.D.E/issues/122)).\n\n### Improved\n- The joy device index in `GUIDEInputJoy*` has been changed from a number to a dropdown. This makes the implicit meaning of `-1` as _Any joystick_ more explicit. It also allows for easy selection of virtual joysticks.\n\n### Fixed\n- The input detector will now properly detect abort input even when this input was already pressed when starting the detection ([#123](https://github.com/godotneers/G.U.I.D.E/pull/119)).\n- When changing mapping contexts, actions will not intermittently report initial values anymore. Instead actions are guaranteed to have the value of their input after the context switch, within the same frame. This avoids issues where actions would briefly report wrong values after a context switch ([#125](https://github.com/godotneers/G.U.I.D.E/issues/125)).\n\n## [0.8.0] - 2025-09-29\n### Added\n- Added magnitude modifier. This is useful to determine the strength of an input vector, e.g. to determine how far a joystick is pushed. \n\n## [0.7.5] - 2025-08-19\n### Fixed\n- Popup menus should now appear at the correct positions in multi-monitor and wide-screen setups ([#88](https://github.com/godotneers/G.U.I.D.E/issues/88), [#108](https://github.com/godotneers/G.U.I.D.E/issues/108)).\n\n\n## [0.7.4] - 2025-08-11\n### Fixed\n- The input formatter should now properly format all keyboard and touch inputs ([#105](https://github.com/godotneers/G.U.I.D.E/issues/105)).\n\n## [0.7.3] - 2025-07-31\n### Fixed\n- When layering mapping contexts G.U.I.D.E will no longer ignore bound inputs ([#94](https://github.com/godotneers/G.U.I.D.E/issues/94)).\n\n## [0.7.2] - 2025-07-29\n### Fixed\n- Input detection will now be properly aborted when an abort input is actuated ([#92](https://github.com/godotneers/G.U.I.D.E/issues/92)).\n\n## [0.7.1] - 2025-07-29\n### Fixed\n- When layering mapping contexts, G.U.I.D.E will no longer duplicate actions ([#89](https://github.com/godotneers/G.U.I.D.E/issues/89), [#93](https://github.com/godotneers/G.U.I.D.E/issues/93)).\n\n## [0.7.0] - 2025-07-01\n### Breaking Changes\n- G.U.I.D.E will now preserve state of triggers when switching mapping contexts. In addition, triggers that become active as a result of a context being activated, will be initialized with the current input. This will avoid issue with triggers stopping to trigger or triggering too often when switching mapping contexts. This fixes some issues introduced with 0.6.0. Since this changes the behaviour of triggers, I declare this a breaking change. Please check your triggers after updating to ensure they still work as expected ([#81](https://github.com/godotneers/G.U.I.D.E/issues/81)).\n\n## [0.6.4] - 2025-06-14\n### Added\n- Added support for the PS5 touchpad to the icon renderer and text provider, so this will now properly display in input prompts ([#74](https://github.com/godotneers/G.U.I.D.E/issues/74)).\n\n### Fixed\n- When pressing keys or buttons so rapidly that both the press and release happen to occur in the same frame, G.U.I.D.E would ignore the press event. This has now been fixed. So if a key or button changes state multiple times in a frame, the result of the first state change is used for evaluation this frame. At the end of the frame, the last state, which was recorded in the frame, is restored. This will technically still drop events (e.g. if a button state changes five times in a frame, only the first and last state change will influence triggers) however for all practical purposes this should be good enough. ([#77](https://github.com/godotneers/G.U.I.D.E/issues/77)).\n\n## [0.6.3] - 2025-05-20\n### Added\n- Added a new example showing how to use controller or keyboard & mouse for a top-down shooter ([#64](https://github.com/godotneers/G.U.I.D.E/issues/64)).\n\n### Improved\n- The mapping editor now shows the action value type next to the action name. This helps debugging unexpected behaviour when using an unsuitable action value type for an action. A big thanks goes to [Jose Ramon Rodriguez](https://github.com/jramonrod) for submitting a PR for this ([#59](https://github.com/godotneers/G.U.I.D.E/issues/59), [#63](https://github.com/godotneers/G.U.I.D.E/pull/63))!\n\n\n### Fixed\n- When detecting input for re-binding the `GUIDEInputDetector` will ensure that G.U.I.D.E's input state still receives events. This avoids issues where the player re-binds a key and this key will not work correctly afterward, because G.U.I.D.E's input state was not updated and now has diverged from the actual input state.\n- G.U.I.D.E will now ignore input mappings with a missing action. A missing action will be shown as a warning in the mapping context editor and at runtime ([#66](https://github.com/godotneers/G.U.I.D.E/issues/66)).\n- The icons for the mouse side buttons now properly reflect the physical position of these buttons. If you still get the wrong icons, please delete the `_guide_cache` folder in your user data folder. This will recreate the icons. You can open your user data folder by going to _Project_ > _Open User Data Folder_ in the Godot editor ([#65](https://github.com/godotneers/G.U.I.D.E/issues/65)).\n\n## [0.6.2] - 2025-05-05\n### Improved\n- The `GUIDEInputDetector` now consumes all input events while it is detecting input. This avoids the problem of input accidentally triggering something else (e.g. the Godot UI) while the input is being detected.\n\n### Fixed\n- The input detector no longer shows errors when the detection is started again from outside code while it is already running.\n\n\n## [0.6.1] - 2025-05-04\n### Fixed\n- The plugin version inside Godot is now properly updated.\n- The plugin now ships with UID files for Godot 4.4. These files have no effect on earlier Godot versions ([#60](https://github.com/godotneers/G.U.I.D.E/issues/60)).\n\n## [0.6.0] - 2025-05-02\n### Breaking Changes\n- The input collection system has received a major overhaul. G.U.I.D.E now uses a centralized input state from which all built-in `GUIDEInput` get updates. This makes the whole input collection a lot more efficient. Before each input event would be sent to each currently active `GUIDEInput`. Now the input is sent to a single collector which notifies each `GUIDEInput` about events that they have subscribed to. This significantly reduces the amount of work that needs to be done for each processed input. In addition to that, it allows for efficient querying of the current input state, so inputs like `GUIDEInputKey` can now work with a lot less calls into the engine. Another benefit is that input state is kept in a central place even when `GUIDEInput`s are deactivated. This allows a smooth change of mapping contexts without losing the current input state. Finally, this is some groundwork required for virtual joysticks which will be added in a future version.\n- `GUIDEInput` no longer have an `_input` method as raw input is now handled in `GUIDEInputState`. Instead `GUIDEInput` now subscribes to signals in `GUIDEInputState`. If you have written your own `GUIDEInput`, this will affect you. Check the [documentation](https://godotneers.github.io/G.U.I.D.E/usage/extending-guide#creating-custom-inputs) for more information on how custom inputs now work. \n- Input is now no longer reset when mapping contexts are swapped out. So if you have two mapping contexts both listening for the key `W` to be pressed down, and you switch from one to the other while the key is pressed down, then the input previously would reset and the player would have to release and press the `W` key again for it to be detected. This is especially annoying in games where you have to hold a key to move or perform an action. Now the input will be kept active and the player can continue to use it without having to press the key again. Since this changes the behaviour of how input is reacting, I declare this a breaking change. If you have mapping contexts which share the same input, carefully check if things still work as intended. You may need to change trigger types (e.g. from _Pressed_ to _Released_) to prevent unwanted input ([#61](https://github.com/godotneers/G.U.I.D.E/issues/61)).\n\n### Improved\n- `GUIDEInputDetector` now automatically disables all mapping contexts when detecting input and restores them afterwards. This avoids accidentally triggering actions while detecting input and until now needed to be done manually. \n- The LICENSE file is now part of the packaged add-on. A big thanks goes to [Simply BLG](https://github.com/SimplyBLGDev) for submitting a PR for this!\n- The test suite has received a good amount of work. It is still not where it could be, but a nice improvement over the previous version. \n\n## [0.5.3] - 2025-04-25\n### Fixed\n- The _Any_ input now properly actuates, if more than one watched input source actuates during the frame in any order ([#57](https://github.com/godotneers/G.U.I.D.E/issues/57)).\n\n## [0.5.2] - 2025-04-14\n### Fixed\n- The _Remap range_ modifier now properly works when the output range is descending and clamp is enabled. Previously this would always return the left end of the output range ([#53](https://github.com/godotneers/G.U.I.D.E/issues/53)). \n\n## [0.5.1] - 2025-04-07\n### Improved\n- `GUIDEInputAny` now has settings to determine minimal mouse and joypad axis motion to be considered as input. This is useful to avoid accidental input when the mouse or joypad only moved slightly ([#26](https://github.com/godotneers/G.U.I.D.E/issues/26), [#52](https://github.com/godotneers/G.U.I.D.E/issues/52)). \n\n## [0.5.0] - 2025-04-01\n### Breaking Change\n- The `GUIDEInputDetector` now detects controller trigger buttons when looking for `BOOLEAN` input. Before it would only do this when detecting `AXIS_1D` input. Since triggers are very often used for boolean input, this behaviour was changed to keep it more in line with player expectations. If you want to keep the old behaviour, you can set the `allow_triggers_for_boolean_actions` property of `GUIDEInputDetector` to `false`. \n\n### Added\n- `GUIDEAction` now has a new `elapsed_ratio` property which can be used to drive hold progress bars. When the mapping context is activated G.U.I.D.E will check if the action has any hold trigger bound to it and provide some information for an `elapsed_ratio` property on the action to update.  This `elapsed_ratio` will go from 0 to 1 as the hold time elapses and stay at 1 while the action is triggered. If the action has no hold trigger, then `elapsed_ratio` will stay at 0 while the action is not triggered, and go to 1 when the action is triggered ([#48](https://github.com/godotneers/G.U.I.D.E/issues/48)). \n\n### Fixed\n- Removed a font file that was unnecessarily embedded into the mapping context editor scene, bloating its file size ([#49](https://github.com/godotneers/G.U.I.D.E/issues/49)).\n\n\n## [0.4.2] - 2025-03-25\n### Fixed\n- G.U.I.D.E now properly detects the PlayStation DualSense controller range as PlayStation controller. A big thanks goes to [laternRaft](https://github.com/laternRaft) for submitting a PR for this and helping with testing ([#43](https://github.com/godotneers/G.U.I.D.E/pull/43)).\n\n## [0.4.1] - 2025-03-13\n### Fixed\n- Binding a modifier key as the key input (e.g. shift for running) will now work even if \"allow additional modifiers\" is set to false ([#34](https://github.com/godotneers/G.U.I.D.E/issues/34)).\n\n## [0.4.0] - 2025-03-06\n### Breaking Changes\n- Removed `get_display_categories` and `get_remappable_actions` from `GUIDEMappingContext`. These methods were added before `GUIDERemapper` was introduced and are now obsolete. If you used these methods, switch to [using `GUIDERemapper` instead](https://godotneers.github.io/G.U.I.D.E/usage/remapping-input) ([#23](https://github.com/godotneers/G.U.I.D.E/issues/23)).\n\n### Improved\n- It is now possible to share both modifiers and triggers between multiple mappings to ensure that all mappings use the same settings. It is also possible to quickly copy a modifier or trigger from one mapping to another with drag & drop ([#31](https://github.com/godotneers/G.U.I.D.E/issues/31)).\n\n### Fixed\n- Cleaned up duplicate UIDs to prevent issues with Godot 4.4. ([#28](https://github.com/godotneers/G.U.I.D.E/issues/28)).\n\n\n\n## [0.3.1] - 2025-02-14\n### Breaking Changes\n- G.U.I.D.E will now continue to process input when the tree is in pause mode. Also, the debugger and all icon rendering facilities will continue to work even when the tree is paused. Technically, this should have been the behaviour from the beginning because games need to remain controllable even when paused. Since the behaviour was changed, I'm declaring this a breaking change. Please check if your game still works as expected in pause mode. If you need to disable input when the game is paused, consider calling `GUIDE.disable_mapping_context()` or add specific checks to your game code to prevent actions while the game is paused ([#20](https://github.com/godotneers/G.U.I.D.E/issues/20)).\n\n\n## [0.3.0] - 2025-02-10\n### Breaking Changes\n- `GUIDEModifierVirtualCursor` now tracks input as pixels rather than a virtual resolution independent value. This avoids the problem that the cursor movement speed is different for the x and y axes. The new implementation keeps a consistent speed on both axes. To achieve resolution independence, the modifier now has a separate setting which controls the screen scaling. Please check the [documentation](https://godotneers.github.io/G.U.I.D.E/reference/modifiers#virtual-cursor-experimental) for more information. Settings of existing projects will be migrated to the closest equivalent settings. Since the behaviour was changed, I declare this a breaking change. Please check your settings after updating and verify that the cursor behaves as expected ([#15](https://github.com/godotneers/G.U.I.D.E/issues/15)).\n\n### Fixed\n- Calling `cleanup` on `GUIDEInputFormatter` will no longer make the instance unusable. Usually it should not be necessary to call `cleanup` manually, but if you do, the formatter will make sure to recreate the necessary nodes when needed.\n\n### Improved\n- Further work on the test suite has been done. This is nowhere near complete, but it is a start. \n\n## [0.2.0] - 2025-01-30 \n### Added\n- It is now possible to override the action name and category for each input mapping, making it easier to remap actions that are mapped to multiple inputs (e.g. WASD for movement) ([#12](https://github.com/godotneers/G.U.I.D.E/issues/12)).\n- `GUIDEInputFormatter` has a new method `cleanup` which allows tearing down the icon rendering infrastructure. This is mainly useful for automated tests, to avoid spurious records of orphan nodes ([#13](https://github.com/godotneers/G.U.I.D.E/issues/13)).\n\n### Improved\n- Icons are now set to scale with the editor scale, so they no longer should be too small on high resolution displays.\n- The debugger will no longer update the UI when it is not visible. \n\n### Fixed\n- The icon rendering infrastructure is now properly cleaned up when disabling the plugin.\n\n## [0.1.3] - 2025-01-19\n### Improved\n- The debugger now shows the action priorities which are derived from analyzing overlapping input. This can help find problems in the action mapping and also gives a bit more information about what G.U.I.D.E does internally.\n\n### Fixed\n- Chains of chorded actions are now properly handled when calculating action priority from overlapping input ([#9](https://github.com/godotneers/G.U.I.D.E/issues/9))\n- The plugin will now only register/unregister the `GUIDE` singleton when being enabled and disabled. This will avoid marking the project as modified just by loading it ([#11](https://github.com/godotneers/G.U.I.D.E/issues/11)).\n- A few small bugs in the examples have been fixed.\n\n## [0.1.2] - 2025-01-11\n### Breaking Changes\n- `GUIDEInputKey` now properly handles modifier keys (like shift, control, etc.). Until now, the handling of additional modifier keys had a bug that allowed you to press the key with a modifier even though _Allow additional modifiers_ was turned off. This has been fixed. However the more common use case is to ignore additional modifiers, so _Allow additional modifiers_ is now `true` by default. G.U.I.D.E cannot reliably migrate this new default for existing bindings, so if you want your existing key bindings to allow additional modifiers, you need to manually enable it for these. All new bindings will allow additional modifiers by default and you can disable this if you don't want it. \n\n### Improved\n- The documentation of `GUIDEInputKey` has been updated to reflect the changes and add missing information about the modifier keys.\n\n### Fixed\n- `GUIDEInputKey` would not properly detect a key release if it was done in a certain sequence with modifier keys ([#7](https://github.com/godotneers/G.U.I.D.E/issues/7)). \n\n## [0.1.1] - 2025-01-10\n### Added\n- The _Any_ input now supports additional input types (mouse movement, joy axes and touch).\n\n## [0.1.0] - 2025-01-09 \n### Breaking Changes\n- The `input_detected` signal of `GUIDEInputDetector` had a typo in it (was `input_dectected`) and was renamed to fix this. If your code uses this signal you will need to update it. Also check your scenes if you connected this signal using the editor.\n\n### Added\n- Support for touch input:\n\t- _Touch Position_  - will track the position of one or more fingers, similar to _Mouse Position_\n\t- _Touch Axis2D_ - will track the position change of one or more fingers, similar to _Mouse Axis2D_\n\t- _Touch Axis1D_ - similar to _Touch Axis2D_ but tracks a single axis, only.\n\t- _Touch Distance_ - tracks distance changes between two fingers. Useful for implementing pinch/zoom gestures.\n\t- _Touch Angle_ - tracks rotation changes between two fingers. Useful for implementing rotation gestures.\n- New trigger type _Stability_ which triggers depending on whether the input has changed after initial actuatation. Useful for touch input to detect taps and drags.\n- A new `touch` example has been added, showing how to use the new touch features to implement camera drags, pinch/zoom and rotation.\n- New modifier _Normalize_ to normalize an input vector.\n\n### Improved\n- `GUIDEInputDetector` can now filter for device types, so you can limit detected input to keyboard, mouse or joystick/gamepad input.\n- `GUIDEInputDetector` now has a `is_detecting` property to find out whether it is currently detecting input.\n- `GUIDEInputDetector` now has a setting determining which joy index should be assigned to detected joy events.\n- `GUIDERemapper` can now also filter for single actions, so it is easier to remap input for a specific action.\n- The _Canvas Coordinates_ modifier can now also convert relative pixel coordinates into relative world coordinates.\n- The `remapping` example's usability for controllers has been improved, showing some techniques on how to make UI more controller friendly.\n\n### Fixed\n- Triggers will no longer consider infinite input values as \"actuating\". Vector.INF is reserved for cases where no value is available.\n- Modifiers now handle infinite input values more gracefully instead of producing `NaN` values.\n- The text provider will no longer try to translate physical keys to labels on mobile platforms where Godot doesn't support this.\n- Inputs of type `GUIDEInputAction` are now properly displayed in the debugger.\n\n## [0.0.4] - 2024-12-28\n### Fixed\n- Fixed broken equality comparison on `GUIDEInputAny` ([#3](https://github.com/godotneers/G.U.I.D.E/issues/3)).\n\n## [0.0.3] - 2024-12-21\n### Added\n- New trigger type _Released_ which triggers when the input is released (the opposite of _Pressed_).\n- New modifier _Virtual Cursor_ which provides a virtual mouse cursor that can be controlled by any input (experimental).\n\n### Changed\n- In the debugger, inputs now show the raw Vector3 coming from the input. This helps understanding modifiers better as we can see the value that is going into the modifier.\n\n### Fixed\n- Added missing equality comparison for `GUIDEInputAny` and `GUIDEInputMousePosition` so these inputs are not needlessly duplicated and updated multiple times per frame.\n- Triggers do no longer reset the action value to zero when they don't fire. They are intended to be orthogonal to the action values, so they should react to changes of the value, rather than modify it (for that we have modifiers).\n- Fixed several smaller rendering problems with rendering items (e.g. broken direction indicator for Xbox controllers and joysticks, right trigger being shown as left trigger, etc.). To clear cached icons, go to _Project_ > _Open user data folder_  and in this folder delete the `_guide_cache` folder. This will recreate icons. \n- Fixed size of the icons which should be 16x16 not 32x32 ([#2](https://github.com/godotneers/G.U.I.D.E/issues/2)).\n\n## [0.0.2] - 2024-12-02\n### Breaking Changes\n- Removed the leftover`get_value_axis_xxx` methods from the `GUIDEAction` class. Use the `value_axis_xxx` property instead.\n- `GUIDEDebugger` is no longer a public node class because it cannot work as a standalone node. Use the provided `guide_debugger.tscn` scene to add a debugger to your project.\n### Added\n- `GUIDEAction` now has additional functions to check whether it is currently in `Completed` or `Ongoing` state.\n- Added two new modifiers: _Curve_ which allows to apply a curve to the input value and _Map range_ which allows to map the input value to a different range. A huge thanks goes to [Regal Media](https://github.com/RegalMedia) for submitting a PR with this feature!\n### Improved\n- The `input_mappings_changed` signal in `GUIDE` now also fires when any joystick is connected or disconnected.\n\n\n## [0.0.1] - 2024-11-17\n- Initial release of the plugin.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contribution guide\n\nFirst off, thanks for considering a contribution to this project!\n\nThe following checklist will help you to get started and make sure your contribution can be merged as quickly as possible:\n\n- Before you start working on a feature or fix, **please create an [issue](https://github.com/godotneers/G.U.I.D.E/issues/new) first**. This way we can discuss the implementation and any potential side effects before you spend time on it. It's no fun to spend hours developing something and then have your PR rejected because it doesn't fit the project goals or because it breaks something else.\n- In your PR please make sure you **only change the files that are necessary** for your fix or feature. Changes to unrelated files make it harder to review and merge your PR.\n- If you have multiple fixes or features, please **create a separate PR for each**. This makes it easier to review and merge them, and problems with one fix/feature will not block the other fixes/features from being merged.\n- Please make sure your code is **formatted according to the [GDScript style guide](https://docs.godotengine.org/en/stable/getting_started/scripting/gdscript/gdscript_styleguide.html)**. It may not be your preferred style, but we really don't want to have multiple styles mixed in the codebase.\n- Please **rebase your PR on the latest on the `main`** branch before submitting it. This makes it easier to compare the changes and avoids merge conflicts. \n- Your contribution must be **licensed under the MIT license**. This is the same license as this project uses. See the [LICENSE](LICENSE) file for details.\n\n\n"
  },
  {
    "path": "LICENSE.md",
    "content": "Copyright (c) 2024-present Jan Thomä\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# G.U.I.D.E - Godot Unified Input Detection Engine\n\nG.U.I.D.E is an extension for the Godot Engine that allows you to easily use input from multiple sources, such as keyboard, mouse, gamepad and touch in a unified way. Gone are the days, where mouse input was handled differently from joysticks and touch was a totally different beast. No matter where the input comes from - your game code works the same way.\n\n_Note: While the features work pretty well, this plugin hasn't seen a lot of use yet, so be prepared for a few rough edges. Also the documentation still needs expansion. Please report any issues you encounter._\n\n## Features\n\n- Unified input detection and handling from multiple sources (keyboard, mouse, gamepad, touch, etc.). All inputs are used in the same way in your game code.\n- Inputs can be modified before being fed into your game code (e.g. for joystick dead-zones, sensitivity, inversion, conversion to 2D/3D coordinates, etc.).\n- Inputs can be assigned to actions and these actions trigger on various conditions (e.g. tap, hold, press, release, combos etc.).\n- Multiple input contexts can be defined, which can be enabled/disabled at runtime. This allows you to easily switch between different input schemes (e.g. in-game, menu, driving, flying, walking, etc.).\n- Overlapping input is automatically prioritized, such that input like _LT+A_ will have precedence over just  _A_.\n- Supports both event-based and polling-based input handling, like Godot's built-in input system.\n- Full support for input rebinding at runtime including collision handling. \n- Built-in support for displaying input prompts in your game. These prompts support complex input combinations (e.g. _LT+A_ or combos like _A > B > A > X > Y_). Prompts can be displayed both as text and as icons. Icons will automatically reflect the actual input device being used (e.g. XBox controller, Playstation controller, keyboard, joystick, etc.).\n- Works nicely alongside Godot's built-in input system, so you can use both in parallel if needed. Can also inject action events into Godot's input system.\n\n\n## Documentation\n\nThe documentation is availabe on the [documentation site](https://godotneers.github.io/G.U.I.D.E/).\n\n\n## Acknowledgements / Licenses\n\nThe input prompts use the great icons made by Nicolae (Xelu) Berbece (https://thoseawesomeguys.com/prompts/) under CC0 License.\n"
  },
  {
    "path": "TODO.md",
    "content": "# To Do\n\nThis lists the currently open to do items in no particular order.\n\n## Open\n- [ ] The stick renderer throws errors about a signal being connected twice. Fix that.\n- [ ] Add a C# API.\n \n## Done\n- [x] Virtual stick device change mid-game support.\n- [x] Prepare for asset library (git attributes, page, etc.)\n- [x] Icon for \"Mouse Position\" input.\n- [x] document all possible inputs and how they map to action values\n- [x] Add icon/text renderers for PS/XBOX/Nintendo controllers\n- [x] Make the fallback icons less ugly.\n- [x] Add functionality for runtime re-binding.\n- [x] Fix icon/text for Any input\n- [x] Fix remapping of WASD to ESDF\n- [x] Implement input consumption and action prioritization.\n- [x] Fix \"incomplete format\" error popping up when rendering an input icon.\n- [x] Implement elapsed/triggered time.\n- [x] Make an editor for setting up the system. The inspector is atrocious.\n- [x] Decide whether to convert into C++ after the prototyping phase is done (decided against it, GDScript is fast enough, easier to maintain and works on all platforms out of the box).\n- [x] Remove get_action_value_xxx from Action (replaced by value_xxx property)\n- [x] Documentation\n    - [x] document customizing the input prompt rendering\n    - [x] document input remapping\n"
  },
  {
    "path": "_assets/.gdignore",
    "content": ""
  },
  {
    "path": "addons/gdUnit4/GdUnitRunner.cfg",
    "content": "{\"included\":{\"res://tests/test_layered_contexts.gd\":[]},\"server_port\":31002,\"skipped\":{},\"version\":\"1.0\"}"
  },
  {
    "path": "addons/gdUnit4/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2023 Mike Schulze\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "addons/gdUnit4/bin/GdUnitBuildTool.gd",
    "content": "#!/usr/bin/env -S godot -s\nextends SceneTree\n\nenum {\n\tINIT,\n\tPROCESSING,\n\tEXIT\n}\n\nconst RETURN_SUCCESS  =   0\nconst RETURN_ERROR    = 100\nconst RETURN_WARNING  = 101\n\nvar _console := CmdConsole.new()\nvar _cmd_options: = CmdOptions.new([\n\tCmdOption.new(\n\t\t\"-scp, --src_class_path\",\n\t\t\"-scp <source_path>\",\n\t\t\"The full class path of the source file.\",\n\t\tTYPE_STRING\n\t),\n\tCmdOption.new(\n\t\t\"-scl, --src_class_line\",\n\t\t\"-scl <line_number>\",\n\t\t\"The selected line number to generate test case.\",\n\t\tTYPE_INT\n\t)\n])\n\nvar _status := INIT\nvar _source_file :String = \"\"\nvar _source_line :int = -1\n\n\nfunc _init() -> void:\n\tvar cmd_parser := CmdArgumentParser.new(_cmd_options, \"GdUnitBuildTool.gd\")\n\tvar result := cmd_parser.parse(OS.get_cmdline_args())\n\tif result.is_error():\n\t\tshow_options()\n\t\texit(RETURN_ERROR, result.error_message());\n\t\treturn\n\n\tvar cmd_options :Array[CmdCommand] = result.value()\n\tfor cmd in cmd_options:\n\t\tif cmd.name() == '-scp':\n\t\t\t_source_file = cmd.arguments()[0]\n\t\t\t_source_file = ProjectSettings.localize_path(ProjectSettings.localize_path(_source_file))\n\t\tif cmd.name() == '-scl':\n\t\t\t_source_line = int(cmd.arguments()[0])\n\t# verify required arguments\n\tif _source_file == \"\":\n\t\texit(RETURN_ERROR, \"missing required argument -scp <source>\")\n\t\treturn\n\tif _source_line == -1:\n\t\texit(RETURN_ERROR, \"missing required argument -scl <number>\")\n\t\treturn\n\t_status = PROCESSING\n\n\nfunc _idle(_delta :float) -> void:\n\tif _status == PROCESSING:\n\t\tvar script := ResourceLoader.load(_source_file) as Script\n\t\tif script == null:\n\t\t\texit(RETURN_ERROR, \"Can't load source file %s!\" % _source_file)\n\t\tvar result := GdUnitTestSuiteBuilder.create(script, _source_line)\n\t\tif result.is_error():\n\t\t\tprint_json_error(result.error_message())\n\t\t\texit(RETURN_ERROR, result.error_message())\n\t\t\treturn\n\t\t_console.prints_color(\"Added testcase: %s\" % result.value(), Color.CORNFLOWER_BLUE)\n\t\tprint_json_result(result.value() as Dictionary)\n\t\texit(RETURN_SUCCESS)\n\n\nfunc exit(code :int, message :String = \"\") -> void:\n\t_status = EXIT\n\tif code == RETURN_ERROR:\n\t\tif not message.is_empty():\n\t\t\t_console.prints_error(message)\n\t\t_console.prints_error(\"Abnormal exit with %d\" % code)\n\telse:\n\t\t_console.prints_color(\"Exit code: %d\" % RETURN_SUCCESS, Color.DARK_SALMON)\n\tquit(code)\n\n\nfunc print_json_result(result :Dictionary) -> void:\n\t# convert back to system path\n\tvar path := ProjectSettings.globalize_path(result[\"path\"] as String)\n\tvar json := 'JSON_RESULT:{\"TestCases\" : [{\"line\":%d, \"path\": \"%s\"}]}' % [result[\"line\"], path]\n\tprints(json)\n\n\nfunc print_json_error(error :String) -> void:\n\tprints('JSON_RESULT:{\"Error\" : \"%s\"}' % error)\n\n\nfunc show_options() -> void:\n\t_console.prints_color(\" Usage:\", Color.DARK_SALMON)\n\t_console.prints_color(\"\tbuild -scp <source_path> -scl <line_number>\", Color.DARK_SALMON)\n\t_console.prints_color(\"-- Options ---------------------------------------------------------------------------------------\",\n\t\tColor.DARK_SALMON).new_line()\n\tfor option in _cmd_options.default_options():\n\t\tdescripe_option(option)\n\n\nfunc descripe_option(cmd_option :CmdOption) -> void:\n\t_console.print_color(\"  %-40s\" % str(cmd_option.commands()), Color.CORNFLOWER_BLUE)\n\t_console.prints_color(cmd_option.description(), Color.LIGHT_GREEN)\n\tif not cmd_option.help().is_empty():\n\t\t_console.prints_color(\"%-4s %s\" % [\"\", cmd_option.help()], Color.DARK_TURQUOISE)\n\t_console.new_line()\n"
  },
  {
    "path": "addons/gdUnit4/bin/GdUnitBuildTool.gd.uid",
    "content": "uid://vidr4ejcbkss\n"
  },
  {
    "path": "addons/gdUnit4/bin/GdUnitCmdTool.gd",
    "content": "#!/usr/bin/env -S godot -s\nextends SceneTree\n\nconst GdUnitTools := preload(\"res://addons/gdUnit4/src/core/GdUnitTools.gd\")\n\n\n#warning-ignore-all:return_value_discarded\nclass CLIRunner:\n\textends Node\n\n\tenum {\n\t\tREADY,\n\t\tINIT,\n\t\tRUN,\n\t\tSTOP,\n\t\tEXIT\n\t}\n\n\tconst DEFAULT_REPORT_COUNT = 20\n\tconst RETURN_SUCCESS = 0\n\tconst RETURN_ERROR = 100\n\tconst RETURN_ERROR_HEADLESS_NOT_SUPPORTED = 103\n\tconst RETURN_ERROR_GODOT_VERSION_NOT_SUPPORTED = 104\n\tconst RETURN_WARNING = 101\n\n\tvar _state := READY\n\tvar _test_suites_to_process: Array\n\tvar _executor :Variant\n\tvar _cs_executor :Variant\n\tvar _report: GdUnitHtmlReport\n\tvar _report_dir: String\n\tvar _report_max: int = DEFAULT_REPORT_COUNT\n\tvar _headless_mode_ignore := false\n\tvar _runner_config := GdUnitRunnerConfig.new()\n\tvar _runner_config_file := \"\"\n\tvar _debug_cmd_args: = PackedStringArray()\n\tvar _console := CmdConsole.new()\n\tvar _cmd_options := CmdOptions.new([\n\t\t\tCmdOption.new(\n\t\t\t\t\"-a, --add\",\n\t\t\t\t\"-a <directory|path of testsuite>\",\n\t\t\t\t\"Adds the given test suite or directory to the execution pipeline.\",\n\t\t\t\tTYPE_STRING\n\t\t\t),\n\t\t\tCmdOption.new(\n\t\t\t\t\"-i, --ignore\",\n\t\t\t\t\"-i <testsuite_name|testsuite_name:test-name>\",\n\t\t\t\t\"Adds the given test suite or test case to the ignore list.\",\n\t\t\t\tTYPE_STRING\n\t\t\t),\n\t\t\tCmdOption.new(\n\t\t\t\t\t\"-c, --continue\",\n\t\t\t\t\t\"\",\n\t\t\t\t\t\"\"\"By default GdUnit will abort checked first test failure to be fail fast,\n\t\t\t\t\tinstead of stop after first failure you can use this option to run the complete test set.\"\"\".dedent()\n\t\t\t),\n\t\t\tCmdOption.new(\n\t\t\t\t\"-conf, --config\",\n\t\t\t\t\"-conf [testconfiguration.cfg]\",\n\t\t\t\t\"Run all tests by given test configuration. Default is 'GdUnitRunner.cfg'\",\n\t\t\t\tTYPE_STRING,\n\t\t\t\ttrue\n\t\t\t),\n\t\t\tCmdOption.new(\n\t\t\t\t\"-help\", \"\",\n\t\t\t\t\"Shows this help message.\"\n\t\t\t),\n\t\t\tCmdOption.new(\"--help-advanced\", \"\",\n\t\t\t\t\"Shows advanced options.\"\n\t\t\t)\n\t\t],\n\t\t[\n\t\t\t# advanced options\n\t\t\tCmdOption.new(\n\t\t\t\t\"-rd, --report-directory\",\n\t\t\t\t\"-rd <directory>\",\n\t\t\t\t\"Specifies the output directory in which the reports are to be written. The default is res://reports/.\",\n\t\t\t\tTYPE_STRING,\n\t\t\t\ttrue\n\t\t\t),\n\t\t\tCmdOption.new(\n\t\t\t\t\"-rc, --report-count\",\n\t\t\t\t\"-rc <count>\",\n\t\t\t\t\"Specifies how many reports are saved before they are deleted. The default is %s.\" % str(DEFAULT_REPORT_COUNT),\n\t\t\t\tTYPE_INT,\n\t\t\t\ttrue\n\t\t\t),\n\t\t\t#CmdOption.new(\"--list-suites\", \"--list-suites [directory]\", \"Lists all test suites located in the given directory.\", TYPE_STRING),\n\t\t\t#CmdOption.new(\"--describe-suite\", \"--describe-suite <suite name>\", \"Shows the description of selected test suite.\", TYPE_STRING),\n\t\t\tCmdOption.new(\n\t\t\t\t\"--info\", \"\",\n\t\t\t\t\"Shows the GdUnit version info\"\n\t\t\t),\n\t\t\tCmdOption.new(\n\t\t\t\t\"--selftest\", \"\",\n\t\t\t\t\"Runs the GdUnit self test\"\n\t\t\t),\n\t\t\tCmdOption.new(\n\t\t\t\t\"--ignoreHeadlessMode\",\n\t\t\t\t\"--ignoreHeadlessMode\",\n\t\t\t\t\"By default, running GdUnit4 in headless mode is not allowed. You can switch off the headless mode check by set this property.\"\n\t\t\t),\n\t\t])\n\n\n\tfunc _ready() -> void:\n\t\t_state = INIT\n\t\t_report_dir = GdUnitFileAccess.current_dir() + \"reports\"\n\t\t_executor = GdUnitTestSuiteExecutor.new()\n\t\t# stop checked first test failure to fail fast\n\t\t@warning_ignore(\"unsafe_cast\")\n\t\t(_executor as GdUnitTestSuiteExecutor).fail_fast(true)\n\t\tif GdUnit4CSharpApiLoader.is_mono_supported():\n\t\t\tprints(\"GdUnit4Net version '%s' loaded.\" % GdUnit4CSharpApiLoader.version())\n\t\t\t_cs_executor = GdUnit4CSharpApiLoader.create_executor(self)\n\t\tvar err := GdUnitSignals.instance().gdunit_event.connect(_on_gdunit_event)\n\t\tif err != OK:\n\t\t\tprints(\"gdUnitSignals failed\")\n\t\t\tpush_error(\"Error checked startup, can't connect executor for 'send_event'\")\n\t\t\tquit(RETURN_ERROR)\n\n\n\tfunc _notification(what: int) -> void:\n\t\tif what == NOTIFICATION_PREDELETE:\n\t\t\tprints(\"Finallize .. done\")\n\n\n\t@warning_ignore(\"unsafe_method_access\")\n\tfunc _process(_delta :float) -> void:\n\t\tmatch _state:\n\t\t\tINIT:\n\t\t\t\tinit_gd_unit()\n\t\t\t\t_state = RUN\n\t\t\tRUN:\n\t\t\t\t# all test suites executed\n\t\t\t\tif _test_suites_to_process.is_empty():\n\t\t\t\t\t_state = STOP\n\t\t\t\telse:\n\t\t\t\t\tset_process(false)\n\t\t\t\t\t# process next test suite\n\t\t\t\t\tvar test_suite: Node = _test_suites_to_process.pop_front()\n\n\t\t\t\t\tif _cs_executor != null and _cs_executor.IsExecutable(test_suite):\n\t\t\t\t\t\t_cs_executor.Execute(test_suite)\n\t\t\t\t\t\tawait _cs_executor.ExecutionCompleted\n\t\t\t\t\telse:\n\t\t\t\t\t\tawait _executor.execute(test_suite)\n\t\t\t\t\tset_process(true)\n\t\t\tSTOP:\n\t\t\t\t_state = EXIT\n\t\t\t\t_on_gdunit_event(GdUnitStop.new())\n\t\t\t\tquit(report_exit_code(_report))\n\n\n\tfunc quit(code: int) -> void:\n\t\t_cs_executor = null\n\t\tGdUnitTools.dispose_all()\n\t\tawait GdUnitMemoryObserver.gc_on_guarded_instances()\n\t\tawait get_tree().physics_frame\n\t\tget_tree().quit(code)\n\n\n\tfunc set_report_dir(path: String) -> void:\n\t\t_report_dir = ProjectSettings.globalize_path(GdUnitFileAccess.make_qualified_path(path))\n\t\t_console.prints_color(\n\t\t\t\"Set write reports to %s\" % _report_dir,\n\t\t\tColor.DEEP_SKY_BLUE\n\t\t)\n\n\n\tfunc set_report_count(count: String) -> void:\n\t\tvar report_count := count.to_int()\n\t\tif report_count < 1:\n\t\t\t_console.prints_error(\n\t\t\t\t\"Invalid report history count '%s' set back to default %d\"\n\t\t\t\t% [count, DEFAULT_REPORT_COUNT]\n\t\t\t)\n\t\t\t_report_max = DEFAULT_REPORT_COUNT\n\t\telse:\n\t\t\t_console.prints_color(\n\t\t\t\t\"Set report history count to %s\" % count,\n\t\t\t\tColor.DEEP_SKY_BLUE\n\t\t\t)\n\t\t\t_report_max = report_count\n\n\n\tfunc disable_fail_fast() -> void:\n\t\t_console.prints_color(\n\t\t\t\"Disabled fail fast!\",\n\t\t\tColor.DEEP_SKY_BLUE\n\t\t)\n\t\t@warning_ignore(\"unsafe_method_access\")\n\t\t_executor.fail_fast(false)\n\n\n\tfunc run_self_test() -> void:\n\t\t_console.prints_color(\n\t\t\t\"Run GdUnit4 self tests.\",\n\t\t\tColor.DEEP_SKY_BLUE\n\t\t)\n\t\tdisable_fail_fast()\n\t\t_runner_config.self_test()\n\n\n\tfunc show_version() -> void:\n\t\t_console.prints_color(\n\t\t\t\"Godot %s\" % Engine.get_version_info().get(\"string\") as String,\n\t\t\tColor.DARK_SALMON\n\t\t)\n\t\tvar config := ConfigFile.new()\n\t\tconfig.load(\"addons/gdUnit4/plugin.cfg\")\n\t\t_console.prints_color(\n\t\t\t\"GdUnit4 %s\" % config.get_value(\"plugin\", \"version\") as String,\n\t\t\tColor.DARK_SALMON\n\t\t)\n\t\tquit(RETURN_SUCCESS)\n\n\n\tfunc check_headless_mode() -> void:\n\t\t_headless_mode_ignore = true\n\n\n\tfunc show_options(show_advanced: bool = false) -> void:\n\t\t_console.prints_color(\n\t\t\t\"\"\"\n\t\t\tUsage:\n\t\t\t\truntest -a <directory|path of testsuite>\n\t\t\t\truntest -a <directory> -i <path of testsuite|testsuite_name|testsuite_name:test_name>\n\t\t\t\t\"\"\".dedent(),\n\t\t\tColor.DARK_SALMON\n\t\t).prints_color(\n\t\t\t\"-- Options ---------------------------------------------------------------------------------------\",\n\t\t\tColor.DARK_SALMON\n\t\t).new_line()\n\t\tfor option in _cmd_options.default_options():\n\t\t\tdescripe_option(option)\n\t\tif show_advanced:\n\t\t\t_console.prints_color(\n\t\t\t\t\"-- Advanced options --------------------------------------------------------------------------\",\n\t\t\t\tColor.DARK_SALMON\n\t\t\t).new_line()\n\t\t\tfor option in _cmd_options.advanced_options():\n\t\t\t\tdescripe_option(option)\n\n\n\tfunc descripe_option(cmd_option: CmdOption) -> void:\n\t\t_console.print_color(\n\t\t\t\"  %-40s\" % str(cmd_option.commands()),\n\t\t\tColor.CORNFLOWER_BLUE\n\t\t)\n\t\t_console.prints_color(\n\t\t\tcmd_option.description(),\n\t\t\tColor.LIGHT_GREEN\n\t\t)\n\t\tif not cmd_option.help().is_empty():\n\t\t\t_console.prints_color(\n\t\t\t\t\"%-4s %s\" % [\"\", cmd_option.help()],\n\t\t\t\tColor.DARK_TURQUOISE\n\t\t\t)\n\t\t_console.new_line()\n\n\n\tfunc load_test_config(path := GdUnitRunnerConfig.CONFIG_FILE) -> void:\n\t\t_console.print_color(\n\t\t\t\"Loading test configuration %s\\n\" % path,\n\t\t\tColor.CORNFLOWER_BLUE\n\t\t)\n\t\t_runner_config_file = path\n\t\t_runner_config.load_config(path)\n\n\n\tfunc show_help() -> void:\n\t\tshow_options()\n\t\tquit(RETURN_SUCCESS)\n\n\n\tfunc show_advanced_help() -> void:\n\t\tshow_options(true)\n\t\tquit(RETURN_SUCCESS)\n\n\n\tfunc get_cmdline_args() -> PackedStringArray:\n\t\tif _debug_cmd_args.is_empty():\n\t\t\treturn OS.get_cmdline_args()\n\t\treturn _debug_cmd_args\n\n\n\tfunc init_gd_unit() -> void:\n\t\t_console.prints_color(\n\t\t\t\"\"\"\n\t\t\t--------------------------------------------------------------------------------------------------\n\t\t\tGdUnit4 Comandline Tool\n\t\t\t--------------------------------------------------------------------------------------------------\"\"\".dedent(),\n\t\t\tColor.DARK_SALMON\n\t\t).new_line()\n\n\t\tvar cmd_parser := CmdArgumentParser.new(_cmd_options, \"GdUnitCmdTool.gd\")\n\t\tvar result := cmd_parser.parse(get_cmdline_args())\n\t\tif result.is_error():\n\t\t\tshow_options()\n\t\t\t_console.prints_error(result.error_message())\n\t\t\t_console.prints_error(\"Abnormal exit with %d\" % RETURN_ERROR)\n\t\t\t_state = STOP\n\t\t\tquit(RETURN_ERROR)\n\t\t\treturn\n\t\tif result.is_empty():\n\t\t\tshow_help()\n\t\t\treturn\n\t\t# build runner config by given commands\n\t\tvar commands :Array[CmdCommand] = []\n\t\t@warning_ignore(\"unsafe_cast\")\n\t\tcommands.append_array(result.value() as Array)\n\t\tresult = (\n\t\t\tCmdCommandHandler.new(_cmd_options)\n\t\t\t\t.register_cb(\"-help\", show_help)\n\t\t\t\t.register_cb(\"--help-advanced\", show_advanced_help)\n\t\t\t\t.register_cb(\"-a\", _runner_config.add_test_suite)\n\t\t\t\t.register_cbv(\"-a\", _runner_config.add_test_suites)\n\t\t\t\t.register_cb(\"-i\", _runner_config.skip_test_suite)\n\t\t\t\t.register_cbv(\"-i\", _runner_config.skip_test_suites)\n\t\t\t\t.register_cb(\"-rd\", set_report_dir)\n\t\t\t\t.register_cb(\"-rc\", set_report_count)\n\t\t\t\t.register_cb(\"--selftest\", run_self_test)\n\t\t\t\t.register_cb(\"-c\", disable_fail_fast)\n\t\t\t\t.register_cb(\"-conf\", load_test_config)\n\t\t\t\t.register_cb(\"--info\", show_version)\n\t\t\t\t.register_cb(\"--ignoreHeadlessMode\", check_headless_mode)\n\t\t\t\t.execute(commands)\n\t\t)\n\t\tif result.is_error():\n\t\t\t_console.prints_error(result.error_message())\n\t\t\t_state = STOP\n\t\t\tquit(RETURN_ERROR)\n\n\t\tif DisplayServer.get_name() == \"headless\":\n\t\t\tif _headless_mode_ignore:\n\t\t\t\t_console.prints_warning(\"\"\"\n\t\t\t\t\tHeadless mode is ignored by option '--ignoreHeadlessMode'\"\n\n\t\t\t\t\tPlease note that tests that use UI interaction do not work correctly in headless mode.\n\t\t\t\t\tGodot 'InputEvents' are not transported by the Godot engine in headless mode and therefore\n\t\t\t\t\thave no effect in the test!\n\t\t\t\t\t\"\"\".dedent()\n\t\t\t\t).new_line()\n\t\t\telse:\n\t\t\t\t_console.prints_error(\"\"\"\n\t\t\t\t\tHeadless mode is not supported!\n\n\t\t\t\t\tPlease note that tests that use UI interaction do not work correctly in headless mode.\n\t\t\t\t\tGodot 'InputEvents' are not transported by the Godot engine in headless mode and therefore\n\t\t\t\t\thave no effect in the test!\n\n\t\t\t\t\tYou can run with '--ignoreHeadlessMode' to swtich off this check.\n\t\t\t\t\t\"\"\".dedent()\n\t\t\t\t).prints_error(\n\t\t\t\t\t\"Abnormal exit with %d\" % RETURN_ERROR_HEADLESS_NOT_SUPPORTED\n\t\t\t\t)\n\t\t\t\tquit(RETURN_ERROR_HEADLESS_NOT_SUPPORTED)\n\t\t\t\treturn\n\n\t\t_test_suites_to_process = load_testsuites(_runner_config)\n\t\tif _test_suites_to_process.is_empty():\n\t\t\t_console.prints_warning(\"No test suites found, abort test run!\")\n\t\t\t_console.prints_color(\"Exit code: %d\" % RETURN_SUCCESS, Color.DARK_SALMON)\n\t\t\t_state = STOP\n\t\t\tquit(RETURN_SUCCESS)\n\t\tvar total_test_count := _collect_test_case_count(_test_suites_to_process)\n\t\t_on_gdunit_event(GdUnitInit.new(_test_suites_to_process.size(), total_test_count))\n\n\n\tfunc load_testsuites(config: GdUnitRunnerConfig) -> Array[Node]:\n\t\tvar test_suites_to_process: Array[Node] = []\n\t\t# Dictionary[String, Dictionary[String, PackedStringArray]]\n\t\tvar to_execute := config.to_execute()\n\t\t# scan for the requested test suites\n\t\tvar ts_scanner := GdUnitTestSuiteScanner.new()\n\t\tfor as_resource_path in to_execute.keys() as Array[String]:\n\t\t\tvar selected_tests: PackedStringArray = to_execute.get(as_resource_path)\n\t\t\tvar scanned_suites := ts_scanner.scan(as_resource_path)\n\t\t\tskip_test_case(scanned_suites, selected_tests)\n\t\t\ttest_suites_to_process.append_array(scanned_suites)\n\t\tskip_suites(test_suites_to_process, config)\n\t\treturn test_suites_to_process\n\n\n\tfunc skip_test_case(test_suites: Array[Node], test_case_names: Array[String]) -> void:\n\t\tif test_case_names.is_empty():\n\t\t\treturn\n\t\tfor test_suite in test_suites:\n\t\t\tfor test_case in test_suite.get_children():\n\t\t\t\tif not test_case_names.has(test_case.get_name()):\n\t\t\t\t\ttest_suite.remove_child(test_case)\n\t\t\t\t\ttest_case.free()\n\n\n\tfunc skip_suites(test_suites: Array[Node], config: GdUnitRunnerConfig) -> void:\n\t\tvar skipped := config.skipped()\n\t\tif skipped.is_empty():\n\t\t\treturn\n\n\t\tfor test_suite in test_suites:\n\t\t\t# skipp c# testsuites for now\n\t\t\tif test_suite.get_script() == null:\n\t\t\t\tcontinue\n\t\t\tskip_suite(test_suite, skipped)\n\n\n\t# Dictionary[String, PackedStringArray]\n\tfunc skip_suite(test_suite: Node, skipped: Dictionary) -> void:\n\t\tvar skipped_suites :Array = skipped.keys()\n\t\tvar suite_name := test_suite.get_name()\n\t\tvar test_suite_path: String = (\n\t\t\ttest_suite.get_meta(\"ResourcePath\") if test_suite.get_script() == null\n\t\t\telse test_suite.get_script().resource_path\n\t\t)\n\t\tfor suite_to_skip: String in skipped_suites:\n\t\t\t# if suite skipped by path or name\n\t\t\tif (\n\t\t\t\tsuite_to_skip == test_suite_path\n\t\t\t\tor (suite_to_skip.is_valid_filename() and suite_to_skip == suite_name)\n\t\t\t):\n\t\t\t\tvar skipped_tests: PackedStringArray = skipped.get(suite_to_skip)\n\t\t\t\tvar skip_reason := \"Excluded by configuration\"\n\t\t\t\t# if no tests skipped test the complete suite is skipped\n\t\t\t\tif skipped_tests.is_empty():\n\t\t\t\t\t_console.prints_warning(\"Mark the entire test suite '%s' as skipped!\" % test_suite_path)\n\t\t\t\t\t@warning_ignore(\"unsafe_property_access\")\n\t\t\t\t\ttest_suite.__is_skipped = true\n\t\t\t\t\t@warning_ignore(\"unsafe_property_access\")\n\t\t\t\t\ttest_suite.__skip_reason = skip_reason\n\t\t\t\telse:\n\t\t\t\t\t# skip tests\n\t\t\t\t\tfor test_to_skip in skipped_tests:\n\t\t\t\t\t\tvar test_case: _TestCase = test_suite.find_child(test_to_skip, true, false)\n\t\t\t\t\t\tif test_case:\n\t\t\t\t\t\t\ttest_case.skip(true, skip_reason)\n\t\t\t\t\t\t\t_console.prints_warning(\"Mark test case '%s':%s as skipped\" % [suite_to_skip, test_to_skip])\n\t\t\t\t\t\telse:\n\t\t\t\t\t\t\t_console.prints_error(\n\t\t\t\t\t\t\t\t\"Can't skip test '%s' checked test suite '%s', no test with given name exists!\"\n\t\t\t\t\t\t\t\t% [test_to_skip, suite_to_skip]\n\t\t\t\t\t\t\t)\n\n\n\tfunc _collect_test_case_count(test_suites: Array[Node]) -> int:\n\t\tvar total: int = 0\n\t\tfor test_suite in test_suites:\n\t\t\ttotal += test_suite.get_child_count()\n\t\treturn total\n\n\n\t# gdlint: disable=function-name\n\tfunc PublishEvent(data: Dictionary) -> void:\n\t\t_on_gdunit_event(GdUnitEvent.new().deserialize(data))\n\n\n\tfunc _on_gdunit_event(event: GdUnitEvent) -> void:\n\t\tmatch event.type():\n\t\t\tGdUnitEvent.INIT:\n\t\t\t\t_report = GdUnitHtmlReport.new(_report_dir, _report_max)\n\t\t\tGdUnitEvent.STOP:\n\t\t\t\tvar report_path := _report.write()\n\t\t\t\t_report.delete_history(_report_max)\n\t\t\t\tJUnitXmlReport.new(_report._report_path, _report.iteration()).write(_report)\n\t\t\t\t_console.prints_color(\n\t\t\t\t\tbuild_executed_test_suite_msg(_report.suite_executed_count(), _report.suite_count()),\n\t\t\t\t\tColor.DARK_SALMON\n\t\t\t\t).prints_color(\n\t\t\t\t\tbuild_executed_test_case_msg(_report.test_executed_count(), _report.test_count()),\n\t\t\t\t\tColor.DARK_SALMON\n\t\t\t\t).prints_color(\n\t\t\t\t\t\"Total time:        %s\" % LocalTime.elapsed(_report.duration()),\n\t\t\t\t\tColor.DARK_SALMON\n\t\t\t\t).prints_color(\n\t\t\t\t\t\"Open Report at: file://%s\" % report_path,\n\t\t\t\t\tColor.CORNFLOWER_BLUE\n\t\t\t\t)\n\t\t\tGdUnitEvent.TESTSUITE_BEFORE:\n\t\t\t\t_report.add_testsuite_report(event.resource_path(), event.suite_name(), event.total_count())\n\t\t\tGdUnitEvent.TESTSUITE_AFTER:\n\t\t\t\t_report.add_testsuite_reports(\n\t\t\t\t\tevent.resource_path(),\n\t\t\t\t\tevent.error_count(),\n\t\t\t\t\tevent.failed_count(),\n\t\t\t\t\tevent.orphan_nodes(),\n\t\t\t\t\tevent.elapsed_time(),\n\t\t\t\t\tevent.reports()\n\t\t\t\t)\n\t\t\tGdUnitEvent.TESTCASE_BEFORE:\n\t\t\t\t_report.add_testcase(event.resource_path(), event.suite_name(), event.test_name())\n\t\t\tGdUnitEvent.TESTCASE_AFTER:\n\t\t\t\t_report.set_testcase_counters(event.resource_path(),\n\t\t\t\t\tevent.test_name(),\n\t\t\t\t\tevent.is_error(),\n\t\t\t\t\tevent.failed_count(),\n\t\t\t\t\tevent.orphan_nodes(),\n\t\t\t\t\tevent.is_skipped(),\n\t\t\t\t\tevent.is_flaky(),\n\t\t\t\t\tevent.elapsed_time())\n\t\t\t\t_report.add_testcase_reports(event.resource_path(), event.test_name(), event.reports())\n\t\t\tGdUnitEvent.TESTCASE_STATISTICS:\n\t\t\t\t_report.update_testsuite_counters(event.resource_path(), event.is_error(), event.failed_count(), event.orphan_nodes(),\\\n\t\t\t\t\tevent.is_skipped(), event.is_flaky(), event.elapsed_time())\n\t\tprint_status(event)\n\n\n\tfunc build_executed_test_suite_msg(executed_count :int, total_count :int) -> String:\n\t\tif executed_count == total_count:\n\t\t\treturn \"Executed test suites: (%d/%d)\" % [executed_count, total_count]\n\t\treturn \"Executed test suites: (%d/%d), %d skipped\" % [executed_count, total_count, (total_count - executed_count)]\n\n\n\tfunc build_executed_test_case_msg(executed_count :int, total_count :int) -> String:\n\t\tif executed_count == total_count:\n\t\t\treturn \"Executed test cases: (%d/%d)\" % [executed_count, total_count]\n\t\treturn \"Executed test cases: (%d/%d), %d skipped\" % [executed_count, total_count, (total_count - executed_count)]\n\n\n\tfunc report_exit_code(report: GdUnitHtmlReport) -> int:\n\t\tif report.error_count() + report.failure_count() > 0:\n\t\t\t_console.prints_color(\"Exit code: %d\" % RETURN_ERROR, Color.FIREBRICK)\n\t\t\treturn RETURN_ERROR\n\t\tif report.orphan_count() > 0:\n\t\t\t_console.prints_color(\"Exit code: %d\" % RETURN_WARNING, Color.GOLDENROD)\n\t\t\treturn RETURN_WARNING\n\t\t_console.prints_color(\"Exit code: %d\" % RETURN_SUCCESS, Color.DARK_SALMON)\n\t\treturn RETURN_SUCCESS\n\n\n\tfunc print_status(event: GdUnitEvent) -> void:\n\t\tmatch event.type():\n\t\t\tGdUnitEvent.TESTSUITE_BEFORE:\n\t\t\t\t_console.prints_color(\n\t\t\t\t\t\"Run Test Suite %s \" % event.resource_path(),\n\t\t\t\t\tColor.ANTIQUE_WHITE\n\t\t\t\t)\n\t\t\tGdUnitEvent.TESTCASE_BEFORE:\n\t\t\t\t_console.print_color(\n\t\t\t\t\t\"\tRun Test: %s > %s :\" % [event.resource_path(), event.test_name()],\n\t\t\t\t\tColor.ANTIQUE_WHITE\n\t\t\t\t).prints_color(\n\t\t\t\t\t\"STARTED\",\n\t\t\t\t\tColor.FOREST_GREEN\n\t\t\t\t).save_cursor()\n\t\t\tGdUnitEvent.TESTCASE_AFTER:\n\t\t\t\t#_console.restore_cursor()\n\t\t\t\t_console.print_color(\n\t\t\t\t\t\"\tRun Test: %s > %s :\" % [event.resource_path(), event.test_name()],\n\t\t\t\t\tColor.ANTIQUE_WHITE\n\t\t\t\t)\n\t\t\t\t_print_status(event)\n\t\t\t\t_print_failure_report(event.reports())\n\t\t\tGdUnitEvent.TESTSUITE_AFTER:\n\t\t\t\t_print_failure_report(event.reports())\n\t\t\t\t_print_status(event)\n\t\t\t\t_console.prints_color(\n\t\t\t\t\t\"Statistics: | %d tests cases | %d error | %d failed | %d flaky | %d skipped | %d orphans |\\n\"\n\t\t\t\t\t% [\n\t\t\t\t\t\t_report.test_count(),\n\t\t\t\t\t\t_report.error_count(),\n\t\t\t\t\t\t_report.failure_count(),\n\t\t\t\t\t\t_report.flaky_count(),\n\t\t\t\t\t\t_report.skipped_count(),\n\t\t\t\t\t\t_report.orphan_count()\n\t\t\t\t\t],\n\t\t\t\t\tColor.ANTIQUE_WHITE\n\t\t\t\t)\n\n\n\tfunc _print_failure_report(reports: Array[GdUnitReport]) -> void:\n\t\tfor report in reports:\n\t\t\tif (\n\t\t\t\treport.is_failure()\n\t\t\t\tor report.is_error()\n\t\t\t\tor report.is_warning()\n\t\t\t\tor report.is_skipped()\n\t\t\t):\n\t\t\t\t_console.prints_color(\n\t\t\t\t\t\"\tReport:\",\n\t\t\t\t\tColor.DARK_TURQUOISE, CmdConsole.BOLD | CmdConsole.UNDERLINE\n\t\t\t\t)\n\t\t\t\tvar text := GdUnitTools.richtext_normalize(str(report))\n\t\t\t\tfor line in text.split(\"\\n\"):\n\t\t\t\t\t_console.prints_color(\"\t\t%s\" % line, Color.DARK_TURQUOISE)\n\t\t_console.new_line()\n\n\n\tfunc _print_status(event: GdUnitEvent) -> void:\n\t\tif event.is_flaky() and event.is_success():\n\t\t\tvar retries :int = event.statistic(GdUnitEvent.RETRY_COUNT)\n\t\t\t_console.print_color(\"FLAKY (%d retries)\" % retries, Color.GREEN_YELLOW, CmdConsole.BOLD | CmdConsole.ITALIC)\n\t\telif event.is_success():\n\t\t\t_console.print_color(\"PASSED\", Color.FOREST_GREEN, CmdConsole.BOLD)\n\t\telif event.is_skipped():\n\t\t\t_console.print_color(\"SKIPPED\", Color.GOLDENROD, CmdConsole.BOLD | CmdConsole.ITALIC)\n\t\telif event.is_failed() or event.is_error():\n\t\t\tvar retries :int = event.statistic(GdUnitEvent.RETRY_COUNT)\n\t\t\tif retries > 1:\n\t\t\t\t_console.print_color(\"FAILED (retry %d)\" % retries, Color.FIREBRICK, CmdConsole.BOLD)\n\t\t\telse:\n\t\t\t\t_console.print_color(\"FAILED\", Color.FIREBRICK, CmdConsole.BOLD)\n\t\telif event.is_warning():\n\t\t\t_console.print_color(\"WARNING\", Color.GOLDENROD, CmdConsole.BOLD | CmdConsole.UNDERLINE)\n\n\t\t_console.prints_color(\n\t\t\t\" %s\" % LocalTime.elapsed(event.elapsed_time()), Color.CORNFLOWER_BLUE\n\t\t)\n\n\nvar _cli_runner :CLIRunner\n\n\nfunc _initialize() -> void:\n\tif Engine.get_version_info().hex < 0x40200:\n\t\tprints(\"GdUnit4 requires a minimum of Godot 4.2.x Version!\")\n\t\tquit(CLIRunner.RETURN_ERROR_GODOT_VERSION_NOT_SUPPORTED)\n\t\treturn\n\tDisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_MINIMIZED)\n\t_cli_runner = CLIRunner.new()\n\troot.add_child(_cli_runner)\n\n\n# do not use print statements on _finalize it results in random crashes\nfunc _finalize() -> void:\n\tqueue_delete(_cli_runner)\n\tif OS.is_stdout_verbose():\n\t\tprints(\"Finallize ..\")\n\t\tprints(\"-Orphan nodes report-----------------------\")\n\t\tWindow.print_orphan_nodes()\n\t\tprints(\"Finallize .. done\")\n"
  },
  {
    "path": "addons/gdUnit4/bin/GdUnitCmdTool.gd.uid",
    "content": "uid://dse8va3nbnsj\n"
  },
  {
    "path": "addons/gdUnit4/bin/GdUnitCopyLog.gd",
    "content": "#!/usr/bin/env -S godot -s\nextends MainLoop\n\nconst GdUnitTools := preload(\"res://addons/gdUnit4/src/core/GdUnitTools.gd\")\n\n# gdlint: disable=max-line-length\nconst LOG_FRAME_TEMPLATE = \"\"\"\n<!DOCTYPE html>\n<html style=\"display: inline-grid;\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n\t<title>Godot Logging</title>\n\t<link rel=\"stylesheet\" href=\"css/styles.css\">\n</head>\n\n<body style=\"background-color: #eee;\">\n\t<div class=\"godot-report-frame\"\">\n${content}\n\t</div>\n</body>\n</html>\n\"\"\"\n\nconst NO_LOG_MESSAGE = \"\"\"\n<h3>No logging available!</h3>\n</br>\n<p>In order for logging to take place, you must activate the Activate file logging option in the project settings.</p>\n<p>You can enable the logging under:\n<b>Project Settings</b> > <b>Debug</b> > <b>File Logging</b> > <b>Enable File Logging</b> in the project settings.</p>\n\"\"\"\n\n#warning-ignore-all:return_value_discarded\nvar _cmd_options := CmdOptions.new([\n\t\tCmdOption.new(\n\t\t\t\"-rd, --report-directory\",\n\t\t\t\"-rd <directory>\",\n\t\t\t\"Specifies the output directory in which the reports are to be written. The default is res://reports/.\",\n\t\t\tTYPE_STRING,\n\t\t\ttrue\n\t\t)\n\t])\n\n\nvar _report_root_path: String\nvar _current_report_path: String\nvar _debug_cmd_args := PackedStringArray()\n\n\nfunc _init() -> void:\n\tset_report_directory(GdUnitFileAccess.current_dir() + \"reports\")\n\tset_current_report_path()\n\n\nfunc _process(_delta: float) -> bool:\n\t# check if reports exists\n\tif not reports_available():\n\t\tprints(\"no reports found\")\n\t\treturn true\n\n\t# only process if godot logging is enabled\n\tif not GdUnitSettings.is_log_enabled():\n\t\twrite_report(NO_LOG_MESSAGE, \"\")\n\t\treturn true\n\n\t# parse possible custom report path,\n\tvar cmd_parser := CmdArgumentParser.new(_cmd_options, \"GdUnitCmdTool.gd\")\n\t# ignore erros and exit quitly\n\tif cmd_parser.parse(get_cmdline_args(), true).is_error():\n\t\treturn true\n\tCmdCommandHandler.new(_cmd_options).register_cb(\"-rd\", set_report_directory)\n\n\tvar godot_log_file := scan_latest_godot_log()\n\tvar result := read_log_file_content(godot_log_file)\n\tif result.is_error():\n\t\twrite_report(result.error_message(), godot_log_file)\n\t\treturn true\n\twrite_report(result.value_as_string(), godot_log_file)\n\treturn true\n\n\nfunc set_current_report_path() -> void:\n\t# scan for latest report directory\n\tvar iteration := GdUnitFileAccess.find_last_path_index(\n\t\t_report_root_path, GdUnitHtmlReport.REPORT_DIR_PREFIX\n\t)\n\t_current_report_path = \"%s/%s%d\" % [_report_root_path, GdUnitHtmlReport.REPORT_DIR_PREFIX, iteration]\n\n\nfunc set_report_directory(path: String) -> void:\n\t_report_root_path = path\n\n\nfunc get_log_report_html() -> String:\n\treturn _current_report_path + \"/godot_report_log.html\"\n\n\nfunc reports_available() -> bool:\n\treturn DirAccess.dir_exists_absolute(_report_root_path)\n\n\nfunc scan_latest_godot_log() -> String:\n\tvar path := GdUnitSettings.get_log_path().get_base_dir()\n\tvar files_sorted := Array()\n\tfor file in GdUnitFileAccess.scan_dir(path):\n\t\tvar file_name := \"%s/%s\" % [path, file]\n\t\tfiles_sorted.append(file_name)\n\t# sort by name, the name contains the timestamp so we sort at the end by timestamp\n\tfiles_sorted.sort()\n\treturn files_sorted.back()\n\n\nfunc read_log_file_content(log_file: String) -> GdUnitResult:\n\tvar file := FileAccess.open(log_file, FileAccess.READ)\n\tif file == null:\n\t\treturn GdUnitResult.error(\n\t\t\t\"Can't find log file '%s'. Error: %s\"\n\t\t\t% [log_file, error_string(FileAccess.get_open_error())]\n\t\t)\n\tvar content := \"<pre>\" + file.get_as_text()\n\t# patch out console format codes\n\tfor color_index in range(0, 256):\n\t\tvar to_replace := \"\u001b[38;5;%dm\" % color_index\n\t\tcontent = content.replace(to_replace, \"\")\n\tcontent += \"</pre>\"\n\tcontent = content\\\n\t\t.replace(\"\u001b[0m\", \"\")\\\n\t\t.replace(CmdConsole.CSI_BOLD, \"\")\\\n\t\t.replace(CmdConsole.CSI_ITALIC, \"\")\\\n\t\t.replace(CmdConsole.CSI_UNDERLINE, \"\")\n\treturn GdUnitResult.success(content)\n\n\nfunc write_report(content: String, godot_log_file: String) -> GdUnitResult:\n\tvar file := FileAccess.open(get_log_report_html(), FileAccess.WRITE)\n\tif file == null:\n\t\treturn GdUnitResult.error(\n\t\t\t\"Can't open to write '%s'. Error: %s\"\n\t\t\t% [get_log_report_html(), error_string(FileAccess.get_open_error())]\n\t\t)\n\tvar report_html := LOG_FRAME_TEMPLATE.replace(\"${content}\", content)\n\tfile.store_string(report_html)\n\t_update_index_html(godot_log_file)\n\treturn GdUnitResult.success(file)\n\n\nfunc _update_index_html(godot_log_file: String) -> void:\n\tvar index_file := FileAccess.open(\"%s/index.html\" % _current_report_path, FileAccess.READ_WRITE)\n\tif index_file == null:\n\t\tpush_error(\n\t\t\t\"Can't add log path to index.html. Error: %s\"\n\t\t\t% error_string(FileAccess.get_open_error())\n\t\t)\n\t\treturn\n\tvar content := index_file.get_as_text()\\\n\t\t.replace(\"${log_report}\", get_log_report_html())\\\n\t\t.replace(\"${godot_log_file}\", godot_log_file)\n\t# overide it\n\tindex_file.seek(0)\n\tindex_file.store_string(content)\n\n\nfunc get_cmdline_args() -> PackedStringArray:\n\tif _debug_cmd_args.is_empty():\n\t\treturn OS.get_cmdline_args()\n\treturn _debug_cmd_args\n"
  },
  {
    "path": "addons/gdUnit4/bin/GdUnitCopyLog.gd.uid",
    "content": "uid://dy0e4k8e7so0l\n"
  },
  {
    "path": "addons/gdUnit4/plugin.cfg",
    "content": "[plugin]\n\nname=\"gdUnit4\"\ndescription=\"Unit Testing Framework for Godot Scripts\"\nauthor=\"Mike Schulze\"\nversion=\"4.5.0\"\nscript=\"plugin.gd\"\n"
  },
  {
    "path": "addons/gdUnit4/plugin.gd",
    "content": "@tool\nextends EditorPlugin\n\nconst GdUnitTools := preload(\"res://addons/gdUnit4/src/core/GdUnitTools.gd\")\nconst GdUnitTestDiscoverGuard := preload(\"res://addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd\")\nconst GdUnitConsole := preload(\"res://addons/gdUnit4/src/ui/GdUnitConsole.gd\")\n\n\nvar _gd_inspector: Control\nvar _gd_console: GdUnitConsole\nvar _guard: GdUnitTestDiscoverGuard\n\n\nfunc _enter_tree() -> void:\n\tif check_running_in_test_env():\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tCmdConsole.new().prints_warning(\"It was recognized that GdUnit4 is running in a test environment, therefore the GdUnit4 plugin will not be executed!\")\n\t\treturn\n\tif Engine.get_version_info().hex < 0x40200:\n\t\tprints(\"GdUnit4 plugin requires a minimum of Godot 4.2.x Version!\")\n\t\treturn\n\tGdUnitSettings.setup()\n\t# Install the GdUnit Inspector\n\t_gd_inspector = (load(\"res://addons/gdUnit4/src/ui/GdUnitInspector.tscn\") as PackedScene).instantiate()\n\tadd_control_to_dock(EditorPlugin.DOCK_SLOT_LEFT_UR, _gd_inspector)\n\t# Install the GdUnit Console\n\t_gd_console = (load(\"res://addons/gdUnit4/src/ui/GdUnitConsole.tscn\") as PackedScene).instantiate()\n\tvar control := add_control_to_bottom_panel(_gd_console, \"gdUnitConsole\")\n\tawait _gd_console.setup_update_notification(control)\n\tif GdUnit4CSharpApiLoader.is_mono_supported():\n\t\tprints(\"GdUnit4Net version '%s' loaded.\" % GdUnit4CSharpApiLoader.version())\n\t# Connect to be notified for script changes to be able to discover new tests\n\t_guard = GdUnitTestDiscoverGuard.new()\n\t@warning_ignore(\"return_value_discarded\")\n\tresource_saved.connect(_on_resource_saved)\n\tprints(\"Loading GdUnit4 Plugin success\")\n\n\nfunc _exit_tree() -> void:\n\tif check_running_in_test_env():\n\t\treturn\n\tif is_instance_valid(_gd_inspector):\n\t\tremove_control_from_docks(_gd_inspector)\n\t\t_gd_inspector.free()\n\tif is_instance_valid(_gd_console):\n\t\tremove_control_from_bottom_panel(_gd_console)\n\t\t_gd_console.free()\n\tGdUnitTools.dispose_all(true)\n\tprints(\"Unload GdUnit4 Plugin success\")\n\n\nfunc check_running_in_test_env() -> bool:\n\tvar args := OS.get_cmdline_args()\n\targs.append_array(OS.get_cmdline_user_args())\n\treturn DisplayServer.get_name() == \"headless\" or args.has(\"--selftest\") or args.has(\"--add\") or args.has(\"-a\") or args.has(\"--quit-after\") or args.has(\"--import\")\n\n\nfunc _on_resource_saved(resource: Resource) -> void:\n\tif resource is Script:\n\t\tawait _guard.discover(resource as Script)\n"
  },
  {
    "path": "addons/gdUnit4/plugin.gd.uid",
    "content": "uid://d2u6j827llq5e\n"
  },
  {
    "path": "addons/gdUnit4/runtest.cmd",
    "content": "@ECHO OFF\nCLS\n\nIF NOT DEFINED GODOT_BIN (\n\tECHO \"GODOT_BIN is not set.\"\n\tECHO \"Please set the environment variable 'setx GODOT_BIN <path to godot.exe>'\"\n\tEXIT /b -1\n)\n\nREM scan if Godot mono used and compile c# classes\nfor /f \"tokens=5 delims=. \" %%i in ('%GODOT_BIN% --version') do set GODOT_TYPE=%%i\nIF \"%GODOT_TYPE%\" == \"mono\" (\n\tECHO \"Godot mono detected\"\n\tECHO Compiling c# classes ... Please Wait\n\tdotnet build --debug\n\tECHO done %errorlevel%\n)\n\n%GODOT_BIN% -s -d res://addons/gdUnit4/bin/GdUnitCmdTool.gd %*\nSET exit_code=%errorlevel%\n%GODOT_BIN% --headless --quiet -s -d res://addons/gdUnit4/bin/GdUnitCopyLog.gd %*\n\nECHO %exit_code%\n\nEXIT /B %exit_code%\n"
  },
  {
    "path": "addons/gdUnit4/runtest.sh",
    "content": "#!/bin/sh\n\nif [ -z \"$GODOT_BIN\" ]; then\n    echo \"'GODOT_BIN' is not set.\"\n    echo \"Please set the environment variable  'export GODOT_BIN=/Applications/Godot.app/Contents/MacOS/Godot'\"\n    exit 1\nfi\n\n\"$GODOT_BIN\" --path . -s -d res://addons/gdUnit4/bin/GdUnitCmdTool.gd $*\nexit_code=$?\necho \"Run tests ends with $exit_code\"\n\n\"$GODOT_BIN\" --headless --path . --quiet -s -d res://addons/gdUnit4/bin/GdUnitCopyLog.gd $* > /dev/null\nexit_code2=$?\nexit $exit_code\n"
  },
  {
    "path": "addons/gdUnit4/src/Comparator.gd",
    "content": "class_name Comparator\nextends Resource\n\nenum {\n\tEQUAL,\n\tLESS_THAN,\n\tLESS_EQUAL,\n\tGREATER_THAN,\n\tGREATER_EQUAL,\n\tBETWEEN_EQUAL,\n\tNOT_BETWEEN_EQUAL,\n}\n"
  },
  {
    "path": "addons/gdUnit4/src/Comparator.gd.uid",
    "content": "uid://ctlh2t38mahwm\n"
  },
  {
    "path": "addons/gdUnit4/src/Fuzzers.gd",
    "content": "## A fuzzer implementation to provide default implementation\nclass_name Fuzzers\nextends Resource\n\n\n## Generates an random string with min/max length and given charset\nstatic func rand_str(min_length: int, max_length :int, charset := StringFuzzer.DEFAULT_CHARSET) -> Fuzzer:\n\treturn StringFuzzer.new(min_length, max_length, charset)\n\n\n## Generates an random integer in a range form to\nstatic func rangei(from: int, to: int) -> Fuzzer:\n\treturn IntFuzzer.new(from, to)\n\n## Generates a randon float within in a given range\nstatic func rangef(from: float, to: float) -> Fuzzer:\n\treturn FloatFuzzer.new(from, to)\n\n## Generates an random Vector2 in a range form to\nstatic func rangev2(from: Vector2, to: Vector2) -> Fuzzer:\n\treturn Vector2Fuzzer.new(from, to)\n\n\n## Generates an random Vector3 in a range form to\nstatic func rangev3(from: Vector3, to: Vector3) -> Fuzzer:\n\treturn Vector3Fuzzer.new(from, to)\n\n## Generates an integer in a range form to that can be divided exactly by 2\nstatic func eveni(from: int, to: int) -> Fuzzer:\n\treturn IntFuzzer.new(from, to, IntFuzzer.EVEN)\n\n## Generates an integer in a range form to that cannot be divided exactly by 2\nstatic func oddi(from: int, to: int) -> Fuzzer:\n\treturn IntFuzzer.new(from, to, IntFuzzer.ODD)\n"
  },
  {
    "path": "addons/gdUnit4/src/Fuzzers.gd.uid",
    "content": "uid://byx7ltrjl833o\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitArrayAssert.gd",
    "content": "## An Assertion Tool to verify array values\nclass_name GdUnitArrayAssert\nextends GdUnitAssert\n\n\n## Verifies that the current value is null.\nfunc is_null() -> GdUnitArrayAssert:\n\treturn self\n\n\n## Verifies that the current value is not null.\nfunc is_not_null() -> GdUnitArrayAssert:\n\treturn self\n\n\n## Verifies that the current Array is equal to the given one.\n@warning_ignore(\"unused_parameter\")\nfunc is_equal(expected :Variant) -> GdUnitArrayAssert:\n\treturn self\n\n\n## Verifies that the current Array is equal to the given one, ignoring case considerations.\n@warning_ignore(\"unused_parameter\")\nfunc is_equal_ignoring_case(expected :Variant) -> GdUnitArrayAssert:\n\treturn self\n\n\n## Verifies that the current Array is not equal to the given one.\n@warning_ignore(\"unused_parameter\")\nfunc is_not_equal(expected :Variant) -> GdUnitArrayAssert:\n\treturn self\n\n\n## Verifies that the current Array is not equal to the given one, ignoring case considerations.\n@warning_ignore(\"unused_parameter\")\nfunc is_not_equal_ignoring_case(expected :Variant) -> GdUnitArrayAssert:\n\treturn self\n\n\n## Verifies that the current Array is empty, it has a size of 0.\nfunc is_empty() -> GdUnitArrayAssert:\n\treturn self\n\n\n## Verifies that the current Array is not empty, it has a size of minimum 1.\nfunc is_not_empty() -> GdUnitArrayAssert:\n\treturn self\n\n## Verifies that the current Array is the same. [br]\n## Compares the current by object reference equals\n@warning_ignore(\"unused_parameter\", \"shadowed_global_identifier\")\nfunc is_same(expected :Variant) -> GdUnitArrayAssert:\n\treturn self\n\n\n## Verifies that the current Array is NOT the same. [br]\n## Compares the current by object reference equals\n@warning_ignore(\"unused_parameter\", \"shadowed_global_identifier\")\nfunc is_not_same(expected :Variant) -> GdUnitArrayAssert:\n\treturn self\n\n\n## Verifies that the current Array has a size of given value.\n@warning_ignore(\"unused_parameter\")\nfunc has_size(expectd: int) -> GdUnitArrayAssert:\n\treturn self\n\n\n## Verifies that the current Array contains the given values, in any order.[br]\n## The values are compared by deep parameter comparision, for object reference compare you have to use [method contains_same]\n@warning_ignore(\"unused_parameter\")\nfunc contains(expected :Variant) -> GdUnitArrayAssert:\n\treturn self\n\n\n## Verifies that the current Array contains exactly only the given values and nothing else, in same order.[br]\n## The values are compared by deep parameter comparision, for object reference compare you have to use [method contains_same_exactly]\n@warning_ignore(\"unused_parameter\")\nfunc contains_exactly(expected :Variant) -> GdUnitArrayAssert:\n\treturn self\n\n\n## Verifies that the current Array contains exactly only the given values and nothing else, in any order.[br]\n## The values are compared by deep parameter comparision, for object reference compare you have to use [method contains_same_exactly_in_any_order]\n@warning_ignore(\"unused_parameter\")\nfunc contains_exactly_in_any_order(expected :Variant) -> GdUnitArrayAssert:\n\treturn self\n\n\n## Verifies that the current Array contains the given values, in any order.[br]\n## The values are compared by object reference, for deep parameter comparision use [method contains]\n@warning_ignore(\"unused_parameter\")\nfunc contains_same(expected :Variant) -> GdUnitArrayAssert:\n\treturn self\n\n\n## Verifies that the current Array contains exactly only the given values and nothing else, in same order.[br]\n## The values are compared by object reference, for deep parameter comparision use [method contains_exactly]\n@warning_ignore(\"unused_parameter\")\nfunc contains_same_exactly(expected :Variant) -> GdUnitArrayAssert:\n\treturn self\n\n\n## Verifies that the current Array contains exactly only the given values and nothing else, in any order.[br]\n## The values are compared by object reference, for deep parameter comparision use [method contains_exactly_in_any_order]\n@warning_ignore(\"unused_parameter\")\nfunc contains_same_exactly_in_any_order(expected :Variant) -> GdUnitArrayAssert:\n\treturn self\n\n\n## Verifies that the current Array do NOT contains the given values, in any order.[br]\n## The values are compared by deep parameter comparision, for object reference compare you have to use [method not_contains_same]\n## [b]Example:[/b]\n## [codeblock]\n## # will succeed\n## assert_array([1, 2, 3, 4, 5]).not_contains([6])\n## # will fail\n## assert_array([1, 2, 3, 4, 5]).not_contains([2, 6])\n## [/codeblock]\n@warning_ignore(\"unused_parameter\")\nfunc not_contains(expected :Variant) -> GdUnitArrayAssert:\n\treturn self\n\n\n## Verifies that the current Array do NOT contains the given values, in any order.[br]\n## The values are compared by object reference, for deep parameter comparision use [method not_contains]\n## [b]Example:[/b]\n## [codeblock]\n## # will succeed\n## assert_array([1, 2, 3, 4, 5]).not_contains([6])\n## # will fail\n## assert_array([1, 2, 3, 4, 5]).not_contains([2, 6])\n## [/codeblock]\n@warning_ignore(\"unused_parameter\")\nfunc not_contains_same(expected :Variant) -> GdUnitArrayAssert:\n\treturn self\n\n\n## Extracts all values by given function name and optional arguments into a new ArrayAssert.\n## If the elements not accessible by `func_name` the value is converted to `\"n.a\"`, expecting null values\n@warning_ignore(\"unused_parameter\")\nfunc extract(func_name: String, args := Array()) -> GdUnitArrayAssert:\n\treturn self\n\n\n## Extracts all values by given extractor's into a new ArrayAssert.\n## If the elements not extractable than the value is converted to `\"n.a\"`, expecting null values\n@warning_ignore(\"unused_parameter\")\nfunc extractv(\n\textractor0 :GdUnitValueExtractor,\n\textractor1 :GdUnitValueExtractor = null,\n\textractor2 :GdUnitValueExtractor = null,\n\textractor3 :GdUnitValueExtractor = null,\n\textractor4 :GdUnitValueExtractor = null,\n\textractor5 :GdUnitValueExtractor = null,\n\textractor6 :GdUnitValueExtractor = null,\n\textractor7 :GdUnitValueExtractor = null,\n\textractor8 :GdUnitValueExtractor = null,\n\textractor9 :GdUnitValueExtractor = null) -> GdUnitArrayAssert:\n\treturn self\n\n\n\n@warning_ignore(\"unused_parameter\")\nfunc override_failure_message(message :String) -> GdUnitArrayAssert:\n\treturn self\n\n\n@warning_ignore(\"unused_parameter\")\nfunc append_failure_message(message :String) -> GdUnitArrayAssert:\n\treturn self\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitArrayAssert.gd.uid",
    "content": "uid://ct0h0f830p6ly\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitAssert.gd",
    "content": "## Base interface of all GdUnit asserts\nclass_name GdUnitAssert\nextends RefCounted\n\n\n## Verifies that the current value is null.\n@warning_ignore(\"untyped_declaration\")\nfunc is_null():\n\treturn self\n\n\n## Verifies that the current value is not null.\n@warning_ignore(\"untyped_declaration\")\nfunc is_not_null():\n\treturn self\n\n\n## Verifies that the current value is equal to expected one.\n@warning_ignore(\"unused_parameter\")\n@warning_ignore(\"untyped_declaration\")\nfunc is_equal(expected: Variant):\n\treturn self\n\n\n## Verifies that the current value is not equal to expected one.\n@warning_ignore(\"unused_parameter\")\n@warning_ignore(\"untyped_declaration\")\nfunc is_not_equal(expected: Variant):\n\treturn self\n\n\n@warning_ignore(\"untyped_declaration\")\nfunc do_fail():\n\treturn self\n\n\n## Overrides the default failure message by given custom message.\n@warning_ignore(\"unused_parameter\")\n@warning_ignore(\"untyped_declaration\")\nfunc override_failure_message(message :String):\n\treturn self\n\n\n## Appends a custom message to the failure message.\n## This can be used to add additional infromations to the generated failure message.\n@warning_ignore(\"unused_parameter\")\n@warning_ignore(\"untyped_declaration\")\nfunc append_failure_message(message :String):\n\treturn self\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitAssert.gd.uid",
    "content": "uid://vubel7soheoo\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitAwaiter.gd",
    "content": "class_name GdUnitAwaiter\nextends RefCounted\n\n\n# Waits for a specified signal in an interval of 50ms sent from the <source>, and terminates with an error after the specified timeout has elapsed.\n# source: the object from which the signal is emitted\n# signal_name: signal name\n# args: the expected signal arguments as an array\n# timeout: the timeout in ms, default is set to 2000ms\nfunc await_signal_on(source :Object, signal_name :String, args :Array = [], timeout_millis :int = 2000) -> Variant:\n\t# fail fast if the given source instance invalid\n\tvar assert_that := GdUnitAssertImpl.new(signal_name)\n\tvar line_number := GdUnitAssertions.get_line_number()\n\tif not is_instance_valid(source):\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tassert_that.report_error(GdAssertMessages.error_await_signal_on_invalid_instance(source, signal_name, args), line_number)\n\t\treturn await (Engine.get_main_loop() as SceneTree).process_frame\n\t# fail fast if the given source instance invalid\n\tif not is_instance_valid(source):\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tassert_that.report_error(GdAssertMessages.error_await_signal_on_invalid_instance(source, signal_name, args), line_number)\n\t\treturn await await_idle_frame()\n\tvar awaiter := GdUnitSignalAwaiter.new(timeout_millis)\n\tvar value :Variant = await awaiter.on_signal(source, signal_name, args)\n\tif awaiter.is_interrupted():\n\t\tvar failure := \"await_signal_on(%s, %s) timed out after %sms\" % [signal_name, args, timeout_millis]\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tassert_that.report_error(failure, line_number)\n\treturn value\n\n\n# Waits for a specified signal sent from the <source> between idle frames and aborts with an error after the specified timeout has elapsed\n# source: the object from which the signal is emitted\n# signal_name: signal name\n# args: the expected signal arguments as an array\n# timeout: the timeout in ms, default is set to 2000ms\nfunc await_signal_idle_frames(source :Object, signal_name :String, args :Array = [], timeout_millis :int = 2000) -> Variant:\n\tvar line_number := GdUnitAssertions.get_line_number()\n\t# fail fast if the given source instance invalid\n\tif not is_instance_valid(source):\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tGdUnitAssertImpl.new(signal_name)\\\n\t\t\t.report_error(GdAssertMessages.error_await_signal_on_invalid_instance(source, signal_name, args), line_number)\n\t\treturn await await_idle_frame()\n\tvar awaiter := GdUnitSignalAwaiter.new(timeout_millis, true)\n\tvar value :Variant = await awaiter.on_signal(source, signal_name, args)\n\tif awaiter.is_interrupted():\n\t\tvar failure := \"await_signal_idle_frames(%s, %s) timed out after %sms\" % [signal_name, args, timeout_millis]\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tGdUnitAssertImpl.new(signal_name).report_error(failure, line_number)\n\treturn value\n\n\n# Waits for for a given amount of milliseconds\n# example:\n#    # waits for 100ms\n#    await GdUnitAwaiter.await_millis(myNode, 100).completed\n# use this waiter and not `await get_tree().create_timer().timeout to prevent errors when a test case is timed out\nfunc await_millis(milliSec :int) -> void:\n\tvar timer :Timer = Timer.new()\n\ttimer.set_name(\"gdunit_await_millis_timer_%d\" % timer.get_instance_id())\n\t(Engine.get_main_loop() as SceneTree).root.add_child(timer)\n\ttimer.add_to_group(\"GdUnitTimers\")\n\ttimer.set_one_shot(true)\n\ttimer.start(milliSec / 1000.0)\n\tawait timer.timeout\n\ttimer.queue_free()\n\n\n# Waits until the next idle frame\nfunc await_idle_frame() -> void:\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitAwaiter.gd.uid",
    "content": "uid://cjk17c1p2qiuo\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitBoolAssert.gd",
    "content": "## An Assertion Tool to verify boolean values\nclass_name GdUnitBoolAssert\nextends GdUnitAssert\n\n\n## Verifies that the current value is null.\nfunc is_null() -> GdUnitBoolAssert:\n\treturn self\n\n\n## Verifies that the current value is not null.\nfunc is_not_null() -> GdUnitBoolAssert:\n\treturn self\n\n\n## Verifies that the current value is equal to the given one.\n@warning_ignore(\"unused_parameter\")\nfunc is_equal(expected :Variant) -> GdUnitBoolAssert:\n\treturn self\n\n\n## Verifies that the current value is not equal to the given one.\n@warning_ignore(\"unused_parameter\")\nfunc is_not_equal(expected :Variant) -> GdUnitBoolAssert:\n\treturn self\n\n\n## Verifies that the current value is true.\nfunc is_true() -> GdUnitBoolAssert:\n\treturn self\n\n\n## Verifies that the current value is false.\nfunc is_false() -> GdUnitBoolAssert:\n\treturn self\n\n\n## Overrides the default failure message by given custom message.\n@warning_ignore(\"unused_parameter\")\nfunc override_failure_message(message :String) -> GdUnitBoolAssert:\n\treturn self\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitBoolAssert.gd.uid",
    "content": "uid://0k6b245p1wiy\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitConstants.gd",
    "content": "class_name GdUnitConstants\nextends RefCounted\n\nconst NO_ARG :Variant = \"<--null-->\"\n\nconst EXPECT_ASSERT_REPORT_FAILURES := \"expect_assert_report_failures\"\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitConstants.gd.uid",
    "content": "uid://cgkr776yh0rvd\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitDictionaryAssert.gd",
    "content": "## An Assertion Tool to verify dictionary\nclass_name GdUnitDictionaryAssert\nextends GdUnitAssert\n\n\n## Verifies that the current value is null.\nfunc is_null() -> GdUnitDictionaryAssert:\n\treturn self\n\n\n## Verifies that the current value is not null.\nfunc is_not_null() -> GdUnitDictionaryAssert:\n\treturn self\n\n\n## Verifies that the current dictionary is equal to the given one, ignoring order.\n@warning_ignore(\"unused_parameter\")\nfunc is_equal(expected :Variant) -> GdUnitDictionaryAssert:\n\treturn self\n\n\n## Verifies that the current dictionary is not equal to the given one, ignoring order.\n@warning_ignore(\"unused_parameter\")\nfunc is_not_equal(expected :Variant) -> GdUnitDictionaryAssert:\n\treturn self\n\n\n## Verifies that the current dictionary is empty, it has a size of 0.\nfunc is_empty() -> GdUnitDictionaryAssert:\n\treturn self\n\n\n## Verifies that the current dictionary is not empty, it has a size of minimum 1.\nfunc is_not_empty() -> GdUnitDictionaryAssert:\n\treturn self\n\n\n## Verifies that the current dictionary is the same. [br]\n## Compares the current by object reference equals\n@warning_ignore(\"unused_parameter\", \"shadowed_global_identifier\")\nfunc is_same(expected :Variant) -> GdUnitDictionaryAssert:\n\treturn self\n\n\n## Verifies that the current dictionary is NOT the same. [br]\n## Compares the current by object reference equals\n@warning_ignore(\"unused_parameter\")\nfunc is_not_same(expected :Variant) -> GdUnitDictionaryAssert:\n\treturn self\n\n\n## Verifies that the current dictionary has a size of given value.\n@warning_ignore(\"unused_parameter\")\nfunc has_size(expected: int) -> GdUnitDictionaryAssert:\n\treturn self\n\n\n## Verifies that the current dictionary contains the given key(s).[br]\n## The keys are compared by deep parameter comparision, for object reference compare you have to use [method contains_same_keys]\n@warning_ignore(\"unused_parameter\")\nfunc contains_keys(expected :Array) -> GdUnitDictionaryAssert:\n\treturn self\n\n\n## Verifies that the current dictionary contains the given key and value.[br]\n## The key and value are compared by deep parameter comparision, for object reference compare you have to use [method contains_same_key_value]\n@warning_ignore(\"unused_parameter\")\nfunc contains_key_value(key :Variant, value :Variant) -> GdUnitDictionaryAssert:\n\treturn self\n\n\n## Verifies that the current dictionary not contains the given key(s).[br]\n## This function is [b]deprecated[/b] you have to use [method not_contains_keys] instead\n@warning_ignore(\"unused_parameter\")\nfunc contains_not_keys(expected :Array) -> GdUnitDictionaryAssert:\n\tpush_warning(\"Deprecated: 'contains_not_keys' is deprectated and will be removed soon, use `not_contains_keys` instead!\")\n\treturn not_contains_keys(expected)\n\n\n## Verifies that the current dictionary not contains the given key(s).[br]\n## The keys are compared by deep parameter comparision, for object reference compare you have to use [method not_contains_same_keys]\n@warning_ignore(\"unused_parameter\")\nfunc not_contains_keys(expected :Array) -> GdUnitDictionaryAssert:\n\treturn self\n\n\n## Verifies that the current dictionary contains the given key(s).[br]\n## The keys are compared by object reference, for deep parameter comparision use [method contains_keys]\n@warning_ignore(\"unused_parameter\")\nfunc contains_same_keys(expected :Array) -> GdUnitDictionaryAssert:\n\treturn self\n\n\n## Verifies that the current dictionary contains the given key and value.[br]\n## The key and value are compared by object reference, for deep parameter comparision use [method contains_key_value]\n@warning_ignore(\"unused_parameter\")\nfunc contains_same_key_value(key :Variant, value :Variant) -> GdUnitDictionaryAssert:\n\treturn self\n\n\n## Verifies that the current dictionary not contains the given key(s).\n## The keys are compared by object reference, for deep parameter comparision use [method not_contains_keys]\n@warning_ignore(\"unused_parameter\")\nfunc not_contains_same_keys(expected :Array) -> GdUnitDictionaryAssert:\n\treturn self\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitDictionaryAssert.gd.uid",
    "content": "uid://bmhd7n6wxvjfx\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitFailureAssert.gd",
    "content": "## An assertion tool to verify GDUnit asserts.\n## This assert is for internal use only, to verify that failed asserts work as expected.\nclass_name GdUnitFailureAssert\nextends GdUnitAssert\n\n\n## Verifies if the executed assert was successful\nfunc is_success() -> GdUnitFailureAssert:\n\treturn self\n\n## Verifies if the executed assert has failed\nfunc is_failed() -> GdUnitFailureAssert:\n\treturn self\n\n\n## Verifies the failure line is equal to expected one.\n@warning_ignore(\"unused_parameter\")\nfunc has_line(expected :int) -> GdUnitFailureAssert:\n\treturn self\n\n\n## Verifies the failure message is equal to expected one.\n@warning_ignore(\"unused_parameter\")\nfunc has_message(expected: String) -> GdUnitFailureAssert:\n\treturn self\n\n\n## Verifies that the failure message starts with the expected message.\n@warning_ignore(\"unused_parameter\")\nfunc starts_with_message(expected: String) -> GdUnitFailureAssert:\n\treturn self\n\n\n## Verifies that the failure message contains the expected message.\n@warning_ignore(\"unused_parameter\")\nfunc contains_message(expected: String) -> GdUnitFailureAssert:\n\treturn self\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitFailureAssert.gd.uid",
    "content": "uid://ctcktx3fpdslo\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitFileAssert.gd",
    "content": "class_name GdUnitFileAssert\nextends GdUnitAssert\n\n\nfunc is_file() -> GdUnitFileAssert:\n\treturn self\n\n\nfunc exists() -> GdUnitFileAssert:\n\treturn self\n\n\nfunc is_script() -> GdUnitFileAssert:\n\treturn self\n\n\n@warning_ignore(\"unused_parameter\")\nfunc contains_exactly(expected_rows :Array) -> GdUnitFileAssert:\n\treturn self\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitFileAssert.gd.uid",
    "content": "uid://786mnwoxl866\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitFloatAssert.gd",
    "content": "## An Assertion Tool to verify float values\nclass_name GdUnitFloatAssert\nextends GdUnitAssert\n\n\n## Verifies that the current String is equal to the given one.\n@warning_ignore(\"unused_parameter\")\nfunc is_equal(expected :Variant) -> GdUnitFloatAssert:\n\treturn self\n\n\n## Verifies that the current String is not equal to the given one.\n@warning_ignore(\"unused_parameter\")\nfunc is_not_equal(expected :Variant) -> GdUnitFloatAssert:\n\treturn self\n\n\n## Verifies that the current and expected value are approximately equal.\n@warning_ignore(\"unused_parameter\", \"shadowed_global_identifier\")\nfunc is_equal_approx(expected :float, approx :float) -> GdUnitFloatAssert:\n\treturn self\n\n\n## Verifies that the current value is less than the given one.\n@warning_ignore(\"unused_parameter\")\nfunc is_less(expected :float) -> GdUnitFloatAssert:\n\treturn self\n\n\n## Verifies that the current value is less than or equal the given one.\n@warning_ignore(\"unused_parameter\")\nfunc is_less_equal(expected :float) -> GdUnitFloatAssert:\n\treturn self\n\n\n## Verifies that the current value is greater than the given one.\n@warning_ignore(\"unused_parameter\")\nfunc is_greater(expected :float) -> GdUnitFloatAssert:\n\treturn self\n\n\n## Verifies that the current value is greater than or equal the given one.\n@warning_ignore(\"unused_parameter\")\nfunc is_greater_equal(expected :float) -> GdUnitFloatAssert:\n\treturn self\n\n\n## Verifies that the current value is negative.\nfunc is_negative() -> GdUnitFloatAssert:\n\treturn self\n\n\n## Verifies that the current value is not negative.\nfunc is_not_negative() -> GdUnitFloatAssert:\n\treturn self\n\n\n## Verifies that the current value is equal to zero.\nfunc is_zero() -> GdUnitFloatAssert:\n\treturn self\n\n\n## Verifies that the current value is not equal to zero.\nfunc is_not_zero() -> GdUnitFloatAssert:\n\treturn self\n\n\n## Verifies that the current value is in the given set of values.\n@warning_ignore(\"unused_parameter\")\nfunc is_in(expected :Array) -> GdUnitFloatAssert:\n\treturn self\n\n\n## Verifies that the current value is not in the given set of values.\n@warning_ignore(\"unused_parameter\")\nfunc is_not_in(expected :Array) -> GdUnitFloatAssert:\n\treturn self\n\n\n## Verifies that the current value is between the given boundaries (inclusive).\n@warning_ignore(\"unused_parameter\")\nfunc is_between(from :float, to :float) -> GdUnitFloatAssert:\n\treturn self\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitFloatAssert.gd.uid",
    "content": "uid://b1pyvxx07ygya\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitFuncAssert.gd",
    "content": "## An Assertion Tool to verify function callback values\nclass_name GdUnitFuncAssert\nextends GdUnitAssert\n\n\n## Verifies that the current value is null.\nfunc is_null() -> GdUnitFuncAssert:\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\treturn self\n\n\n## Verifies that the current value is not null.\nfunc is_not_null() -> GdUnitFuncAssert:\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\treturn self\n\n\n## Verifies that the current value is equal to the given one.\n@warning_ignore(\"unused_parameter\")\nfunc is_equal(expected :Variant) -> GdUnitFuncAssert:\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\treturn self\n\n\n## Verifies that the current value is not equal to the given one.\n@warning_ignore(\"unused_parameter\")\nfunc is_not_equal(expected :Variant) -> GdUnitFuncAssert:\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\treturn self\n\n\n## Verifies that the current value is true.\nfunc is_true() -> GdUnitFuncAssert:\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\treturn self\n\n\n## Verifies that the current value is false.\nfunc is_false() -> GdUnitFuncAssert:\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\treturn self\n\n\n## Overrides the default failure message by given custom message.\n@warning_ignore(\"unused_parameter\")\nfunc override_failure_message(message :String) -> GdUnitFuncAssert:\n\treturn self\n\n\n## Sets the timeout in ms to wait the function returnd the expected value, if the time over a failure is emitted.[br]\n## e.g.[br]\n## do wait until 5s the function `is_state` is returns 10 [br]\n## [code]assert_func(instance, \"is_state\").wait_until(5000).is_equal(10)[/code]\n@warning_ignore(\"unused_parameter\")\nfunc wait_until(timeout :int) -> GdUnitFuncAssert:\n\treturn self\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitFuncAssert.gd.uid",
    "content": "uid://c383gmxgtgbmn\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitGodotErrorAssert.gd",
    "content": "## An assertion tool to verify for Godot runtime errors like assert() and push notifications like push_error().\nclass_name GdUnitGodotErrorAssert\nextends GdUnitAssert\n\n\n## Verifies if the executed code runs without any runtime errors\n## Usage:\n##     [codeblock]\n##\t\tawait assert_error(<callable>).is_success()\n##     [/codeblock]\nfunc is_success() -> GdUnitGodotErrorAssert:\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\treturn self\n\n\n## Verifies if the executed code runs into a runtime error\n## Usage:\n##     [codeblock]\n##\t\tawait assert_error(<callable>).is_runtime_error(<expected error message>)\n##     [/codeblock]\n@warning_ignore(\"unused_parameter\")\nfunc is_runtime_error(expected_error :String) -> GdUnitGodotErrorAssert:\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\treturn self\n\n\n## Verifies if the executed code has a push_warning() used\n## Usage:\n##     [codeblock]\n##\t\tawait assert_error(<callable>).is_push_warning(<expected push warning message>)\n##     [/codeblock]\n@warning_ignore(\"unused_parameter\")\nfunc is_push_warning(expected_warning :String) -> GdUnitGodotErrorAssert:\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\treturn self\n\n\n## Verifies if the executed code has a push_error() used\n## Usage:\n##     [codeblock]\n##\t\tawait assert_error(<callable>).is_push_error(<expected push error message>)\n##     [/codeblock]\n@warning_ignore(\"unused_parameter\")\nfunc is_push_error(expected_error :String) -> GdUnitGodotErrorAssert:\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\treturn self\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitGodotErrorAssert.gd.uid",
    "content": "uid://cl8u3n4ao8fhg\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitIntAssert.gd",
    "content": "## An Assertion Tool to verify integer values\nclass_name GdUnitIntAssert\nextends GdUnitAssert\n\n## Verifies that the current String is equal to the given one.\n@warning_ignore(\"unused_parameter\")\nfunc is_equal(expected :Variant) -> GdUnitIntAssert:\n\treturn self\n\n\n## Verifies that the current String is not equal to the given one.\n@warning_ignore(\"unused_parameter\")\nfunc is_not_equal(expected :Variant) -> GdUnitIntAssert:\n\treturn self\n\n\n## Verifies that the current value is less than the given one.\n@warning_ignore(\"unused_parameter\")\nfunc is_less(expected :int) -> GdUnitIntAssert:\n\treturn self\n\n\n## Verifies that the current value is less than or equal the given one.\n@warning_ignore(\"unused_parameter\")\nfunc is_less_equal(expected :int) -> GdUnitIntAssert:\n\treturn self\n\n\n## Verifies that the current value is greater than the given one.\n@warning_ignore(\"unused_parameter\")\nfunc is_greater(expected :int) -> GdUnitIntAssert:\n\treturn self\n\n\n## Verifies that the current value is greater than or equal the given one.\n@warning_ignore(\"unused_parameter\")\nfunc is_greater_equal(expected :int) -> GdUnitIntAssert:\n\treturn self\n\n\n## Verifies that the current value is even.\nfunc is_even() -> GdUnitIntAssert:\n\treturn self\n\n\n## Verifies that the current value is odd.\nfunc is_odd() -> GdUnitIntAssert:\n\treturn self\n\n\n## Verifies that the current value is negative.\nfunc is_negative() -> GdUnitIntAssert:\n\treturn self\n\n\n## Verifies that the current value is not negative.\nfunc is_not_negative() -> GdUnitIntAssert:\n\treturn self\n\n\n## Verifies that the current value is equal to zero.\nfunc is_zero() -> GdUnitIntAssert:\n\treturn self\n\n\n## Verifies that the current value is not equal to zero.\nfunc is_not_zero() -> GdUnitIntAssert:\n\treturn self\n\n\n## Verifies that the current value is in the given set of values.\n@warning_ignore(\"unused_parameter\")\nfunc is_in(expected :Array) -> GdUnitIntAssert:\n\treturn self\n\n\n## Verifies that the current value is not in the given set of values.\n@warning_ignore(\"unused_parameter\")\nfunc is_not_in(expected :Array) -> GdUnitIntAssert:\n\treturn self\n\n\n## Verifies that the current value is between the given boundaries (inclusive).\n@warning_ignore(\"unused_parameter\")\nfunc is_between(from :int, to :int) -> GdUnitIntAssert:\n\treturn self\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitIntAssert.gd.uid",
    "content": "uid://k651n7cj74d2\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitObjectAssert.gd",
    "content": "## An Assertion Tool to verify Object values\nclass_name GdUnitObjectAssert\nextends GdUnitAssert\n\n\n## Verifies that the current value is equal to expected one.\n@warning_ignore(\"unused_parameter\")\nfunc is_equal(expected :Variant) -> GdUnitObjectAssert:\n\treturn self\n\n\n## Verifies that the current value is not equal to expected one.\n@warning_ignore(\"unused_parameter\")\nfunc is_not_equal(expected :Variant) -> GdUnitObjectAssert:\n\treturn self\n\n\n## Verifies that the current value is null.\nfunc is_null() -> GdUnitObjectAssert:\n\treturn self\n\n\n## Verifies that the current value is not null.\nfunc is_not_null() -> GdUnitObjectAssert:\n\treturn self\n\n\n## Verifies that the current value is the same as the given one.\n@warning_ignore(\"unused_parameter\", \"shadowed_global_identifier\")\nfunc is_same(expected :Variant) -> GdUnitObjectAssert:\n\treturn self\n\n\n## Verifies that the current value is not the same as the given one.\n@warning_ignore(\"unused_parameter\")\nfunc is_not_same(expected :Variant) -> GdUnitObjectAssert:\n\treturn self\n\n\n## Verifies that the current value is an instance of the given type.\n@warning_ignore(\"unused_parameter\")\nfunc is_instanceof(expected :Object) -> GdUnitObjectAssert:\n\treturn self\n\n\n## Verifies that the current value is not an instance of the given type.\n@warning_ignore(\"unused_parameter\")\nfunc is_not_instanceof(expected :Variant) -> GdUnitObjectAssert:\n\treturn self\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitObjectAssert.gd.uid",
    "content": "uid://bqe5pqgo35ub3\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitResultAssert.gd",
    "content": "## An Assertion Tool to verify Results\nclass_name GdUnitResultAssert\nextends GdUnitAssert\n\n\n## Verifies that the current value is null.\nfunc is_null() -> GdUnitResultAssert:\n\treturn self\n\n\n## Verifies that the current value is not null.\nfunc is_not_null() -> GdUnitResultAssert:\n\treturn self\n\n\n## Verifies that the result is ends up with empty\nfunc is_empty() -> GdUnitResultAssert:\n\treturn self\n\n\n## Verifies that the result is ends up with success\nfunc is_success() -> GdUnitResultAssert:\n\treturn self\n\n\n## Verifies that the result is ends up with warning\nfunc is_warning() -> GdUnitResultAssert:\n\treturn self\n\n\n## Verifies that the result is ends up with error\nfunc is_error() -> GdUnitResultAssert:\n\treturn self\n\n\n## Verifies that the result contains the given message\n@warning_ignore(\"unused_parameter\")\nfunc contains_message(expected :String) -> GdUnitResultAssert:\n\treturn self\n\n\n## Verifies that the result contains the given value\n@warning_ignore(\"unused_parameter\")\nfunc is_value(expected :Variant) -> GdUnitResultAssert:\n\treturn self\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitResultAssert.gd.uid",
    "content": "uid://pycd4o660wkt\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitSceneRunner.gd",
    "content": "## The Scene Runner is a tool used for simulating interactions on a scene.\n## With this tool, you can simulate input events such as keyboard or mouse input and/or simulate scene processing over a certain number of frames.\n## This tool is typically used for integration testing a scene.\nclass_name GdUnitSceneRunner\nextends RefCounted\n\nconst NO_ARG = GdUnitConstants.NO_ARG\n\n\n## Simulates that an action has been pressed.[br]\n## [member action] : the action e.g. [code]\"ui_up\"[/code][br]\n@warning_ignore(\"unused_parameter\")\nfunc simulate_action_pressed(action: String) -> GdUnitSceneRunner:\n\treturn self\n\n\n## Simulates that an action is pressed.[br]\n## [member action] : the action e.g. [code]\"ui_up\"[/code][br]\n@warning_ignore(\"unused_parameter\")\nfunc simulate_action_press(action: String) -> GdUnitSceneRunner:\n\treturn self\n\n\n## Simulates that an action has been released.[br]\n## [member action] : the action e.g. [code]\"ui_up\"[/code][br]\n@warning_ignore(\"unused_parameter\")\nfunc simulate_action_release(action: String) -> GdUnitSceneRunner:\n\treturn self\n\n\n## Simulates that a key has been pressed.[br]\n## [member key_code] : the key code e.g. [constant KEY_ENTER][br]\n## [member shift_pressed] : false by default set to true if simmulate shift is press[br]\n## [member ctrl_pressed] : false by default set to true if simmulate control is press[br]\n## [codeblock]\n##    func test_key_presssed():\n##       var runner = scene_runner(\"res://scenes/simple_scene.tscn\")\n##       await runner.simulate_key_pressed(KEY_SPACE)\n## [/codeblock]\n@warning_ignore(\"unused_parameter\")\nfunc simulate_key_pressed(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner:\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\treturn self\n\n\n## Simulates that a key is pressed.[br]\n## [member key_code] : the key code e.g. [constant KEY_ENTER][br]\n## [member shift_pressed] : false by default set to true if simmulate shift is press[br]\n## [member ctrl_pressed] : false by default set to true if simmulate control is press[br]\n@warning_ignore(\"unused_parameter\")\nfunc simulate_key_press(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner:\n\treturn self\n\n\n## Simulates that a key has been released.[br]\n## [member key_code] : the key code e.g. [constant KEY_ENTER][br]\n## [member shift_pressed] : false by default set to true if simmulate shift is press[br]\n## [member ctrl_pressed] : false by default set to true if simmulate control is press[br]\n@warning_ignore(\"unused_parameter\")\nfunc simulate_key_release(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner:\n\treturn self\n\n\n## Sets the mouse cursor to given position relative to the viewport.\n## @deprecated: Use [set_mouse_position] instead.\n@warning_ignore(\"unused_parameter\")\nfunc set_mouse_pos(position: Vector2) -> GdUnitSceneRunner:\n\treturn self\n\n\n## Sets the mouse position to the specified vector, provided in pixels and relative to an origin at the upper left corner of the currently focused Window Manager game window.[br]\n## [member position] : The absolute position in pixels as Vector2\n@warning_ignore(\"unused_parameter\")\nfunc set_mouse_position(position: Vector2) -> GdUnitSceneRunner:\n\treturn self\n\n\n## Returns the mouse's position in this Viewport using the coordinate system of this Viewport.\nfunc get_mouse_position() -> Vector2:\n\treturn Vector2.ZERO\n\n\n## Gets the current global mouse position of the current window\nfunc get_global_mouse_position() -> Vector2:\n\treturn Vector2.ZERO\n\n\n## Simulates a mouse moved to final position.[br]\n## [member position] : The final mouse position\n@warning_ignore(\"unused_parameter\")\nfunc simulate_mouse_move(position: Vector2) -> GdUnitSceneRunner:\n\treturn self\n\n\n## Simulates a mouse move to the relative coordinates (offset).[br]\n## [color=yellow]You must use [b]await[/b] to wait until the simulated mouse movement is complete.[/color][br]\n## [br]\n## [member relative] : The relative position, indicating the mouse position offset.[br]\n## [member time] : The time to move the mouse by the relative position in seconds (default is 1 second).[br]\n## [member trans_type] : Sets the type of transition used (default is TRANS_LINEAR).[br]\n## [codeblock]\n##    func test_move_mouse():\n##       var runner = scene_runner(\"res://scenes/simple_scene.tscn\")\n##       await runner.simulate_mouse_move_relative(Vector2(100,100))\n## [/codeblock]\n@warning_ignore(\"unused_parameter\")\nfunc simulate_mouse_move_relative(relative: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner:\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\treturn self\n\n\n## Simulates a mouse move to the absolute coordinates.[br]\n## [color=yellow]You must use [b]await[/b] to wait until the simulated mouse movement is complete.[/color][br]\n## [br]\n## [member position] : The final position of the mouse.[br]\n## [member time] : The time to move the mouse to the final position in seconds (default is 1 second).[br]\n## [member trans_type] : Sets the type of transition used (default is TRANS_LINEAR).[br]\n## [codeblock]\n##    func test_move_mouse():\n##       var runner = scene_runner(\"res://scenes/simple_scene.tscn\")\n##       await runner.simulate_mouse_move_absolute(Vector2(100,100))\n## [/codeblock]\n@warning_ignore(\"unused_parameter\")\nfunc simulate_mouse_move_absolute(position: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner:\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\treturn self\n\n\n## Simulates a mouse button pressed.[br]\n## [member button_index] : The mouse button identifier, one of the [enum MouseButton] or button wheel constants.\n## [member double_click] : Set to true to simulate a double-click\n@warning_ignore(\"unused_parameter\")\nfunc simulate_mouse_button_pressed(button_index: MouseButton, double_click := false) -> GdUnitSceneRunner:\n\treturn self\n\n\n## Simulates a mouse button press (holding)[br]\n## [member button_index] : The mouse button identifier, one of the [enum MouseButton] or button wheel constants.\n## [member double_click] : Set to true to simulate a double-click\n@warning_ignore(\"unused_parameter\")\nfunc simulate_mouse_button_press(button_index: MouseButton, double_click := false) -> GdUnitSceneRunner:\n\treturn self\n\n\n## Simulates a mouse button released.[br]\n## [member button_index] : The mouse button identifier, one of the [enum MouseButton] or button wheel constants.\n@warning_ignore(\"unused_parameter\")\nfunc simulate_mouse_button_release(button_index: MouseButton) -> GdUnitSceneRunner:\n\treturn self\n\n\n## Simulates a screen touch is pressed.[br]\n## [member index] : The touch index in the case of a multi-touch event.[br]\n## [member position] : The position to touch the screen.[br]\n## [member double_tap] : If true, the touch's state is a double tab.\n@warning_ignore(\"unused_parameter\")\nfunc simulate_screen_touch_pressed(index: int, position: Vector2, double_tap := false) -> GdUnitSceneRunner:\n\treturn self\n\n\n## Simulates a screen touch press without releasing it immediately, effectively simulating a \"hold\" action.[br]\n## [member index] : The touch index in the case of a multi-touch event.[br]\n## [member position] : The position to touch the screen.[br]\n## [member double_tap] : If true, the touch's state is a double tab.\n@warning_ignore(\"unused_parameter\")\nfunc simulate_screen_touch_press(index: int, position: Vector2, double_tap := false) -> GdUnitSceneRunner:\n\treturn self\n\n\n## Simulates a screen touch is released.[br]\n## [member index] : The touch index in the case of a multi-touch event.[br]\n## [member double_tap] : If true, the touch's state is a double tab.\n@warning_ignore(\"unused_parameter\")\nfunc simulate_screen_touch_release(index: int, double_tap := false) -> GdUnitSceneRunner:\n\treturn self\n\n\n## Simulates a touch drag and drop event to a relative position.[br]\n## [color=yellow]You must use [b]await[/b] to wait until the simulated drag&drop is complete.[/color][br]\n## [br]\n## [member index] : The touch index in the case of a multi-touch event.[br]\n## [member relative] : The relative position, indicating the drag&drop position offset.[br]\n## [member time] : The time to move to the relative position in seconds (default is 1 second).[br]\n## [member trans_type] : Sets the type of transition used (default is TRANS_LINEAR).[br]\n## [codeblock]\n##    func test_touch_drag_drop():\n##       var runner = scene_runner(\"res://scenes/simple_scene.tscn\")\n##       # start drag at position 50,50\n##       runner.simulate_screen_touch_drag_begin(1, Vector2(50, 50))\n##       # and drop it at final at 150,50  relative (50,50 + 100,0)\n##       await runner.simulate_screen_touch_drag_relative(1, Vector2(100,0))\n## [/codeblock]\n@warning_ignore(\"unused_parameter\")\nfunc simulate_screen_touch_drag_relative(index: int, relative: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner:\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\treturn self\n\n\n## Simulates a touch screen drop to the absolute coordinates (offset).[br]\n## [color=yellow]You must use [b]await[/b] to wait until the simulated drop is complete.[/color][br]\n## [br]\n## [member index] : The touch index in the case of a multi-touch event.[br]\n## [member position] : The final position, indicating the drop position.[br]\n## [member time] : The time to move to the final position in seconds (default is 1 second).[br]\n## [member trans_type] : Sets the type of transition used (default is TRANS_LINEAR).[br]\n## [codeblock]\n##    func test_touch_drag_drop():\n##       var runner = scene_runner(\"res://scenes/simple_scene.tscn\")\n##       # start drag at position 50,50\n##       runner.simulate_screen_touch_drag_begin(1, Vector2(50, 50))\n##       # and drop it at 100,50\n##       await runner.simulate_screen_touch_drag_absolute(1, Vector2(100,50))\n## [/codeblock]\n@warning_ignore(\"unused_parameter\")\nfunc simulate_screen_touch_drag_absolute(index: int, position: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner:\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\treturn self\n\n\n## Simulates a complete drag and drop event from one position to another.[br]\n## This is ideal for testing complex drag-and-drop scenarios that require a specific start and end position.[br]\n## [color=yellow]You must use [b]await[/b] to wait until the simulated drop is complete.[/color][br]\n## [br]\n## [member index] : The touch index in the case of a multi-touch event.[br]\n## [member position] : The drag start position, indicating the drag position.[br]\n## [member drop_position] : The drop position, indicating the drop position.[br]\n## [member time] : The time to move to the final position in seconds (default is 1 second).[br]\n## [member trans_type] : Sets the type of transition used (default is TRANS_LINEAR).[br]\n## [codeblock]\n##    func test_touch_drag_drop():\n##       var runner = scene_runner(\"res://scenes/simple_scene.tscn\")\n##       # start drag at position 50,50 and drop it at 100,50\n##       await runner.simulate_screen_touch_drag_drop(1, Vector2(50, 50), Vector2(100,50))\n## [/codeblock]\n@warning_ignore(\"unused_parameter\")\nfunc simulate_screen_touch_drag_drop(index: int, position: Vector2, drop_position: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner:\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\treturn self\n\n\n## Simulates a touch screen drag event to given position.[br]\n## [member index] : The touch index in the case of a multi-touch event.[br]\n## [member position] : The drag start position, indicating the drag position.[br]\n@warning_ignore(\"unused_parameter\")\nfunc simulate_screen_touch_drag(index: int, position: Vector2) -> GdUnitSceneRunner:\n\treturn self\n\n\n## Returns the actual position of the touchscreen drag position by given index.\n## [member index] : The touch index in the case of a multi-touch event.[br]\n@warning_ignore(\"unused_parameter\")\nfunc get_screen_touch_drag_position(index: int) -> Vector2:\n\treturn Vector2.ZERO\n\n\n## Sets how fast or slow the scene simulation is processed (clock ticks versus the real).[br]\n## It defaults to 1.0. A value of 2.0 means the game moves twice as fast as real life,\n## whilst a value of 0.5 means the game moves at half the regular speed.\n\n\n## Sets the time factor for the scene simulation.\n## [member time_factor] : A float representing the simulation speed.[br]\n## - Default is 1.0, meaning the simulation runs at normal speed.[br]\n## - A value of 2.0 means the simulation runs twice as fast as real time.[br]\n## - A value of 0.5 means the simulation runs at half the regular speed.[br]\n@warning_ignore(\"unused_parameter\")\nfunc set_time_factor(time_factor: float = 1.0) -> GdUnitSceneRunner:\n\treturn self\n\n\n## Simulates scene processing for a certain number of frames.[br]\n## [member frames] : amount of frames to process[br]\n## [member delta_milli] : the time delta between a frame in milliseconds\n@warning_ignore(\"unused_parameter\")\nfunc simulate_frames(frames: int, delta_milli: int = -1) -> GdUnitSceneRunner:\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\treturn self\n\n\n## Simulates scene processing until the given signal is emitted by the scene.[br]\n## [member signal_name] : the signal to stop the simulation[br]\n## [member args] : optional signal arguments to be matched for stop[br]\n@warning_ignore(\"unused_parameter\")\nfunc simulate_until_signal(\n\tsignal_name: String,\n\targ0: Variant = NO_ARG,\n\targ1: Variant = NO_ARG,\n\targ2: Variant = NO_ARG,\n\targ3: Variant = NO_ARG,\n\targ4: Variant = NO_ARG,\n\targ5: Variant = NO_ARG,\n\targ6: Variant = NO_ARG,\n\targ7: Variant = NO_ARG,\n\targ8: Variant = NO_ARG,\n\targ9: Variant = NO_ARG) -> GdUnitSceneRunner:\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\treturn self\n\n\n## Simulates scene processing until the given signal is emitted by the given object.[br]\n## [member source] : the object that should emit the signal[br]\n## [member signal_name] : the signal to stop the simulation[br]\n## [member args] : optional signal arguments to be matched for stop\n@warning_ignore(\"unused_parameter\")\nfunc simulate_until_object_signal(\n\tsource: Object,\n\tsignal_name: String,\n\targ0: Variant = NO_ARG,\n\targ1: Variant = NO_ARG,\n\targ2: Variant = NO_ARG,\n\targ3: Variant = NO_ARG,\n\targ4: Variant = NO_ARG,\n\targ5: Variant = NO_ARG,\n\targ6: Variant = NO_ARG,\n\targ7: Variant = NO_ARG,\n\targ8: Variant = NO_ARG,\n\targ9: Variant = NO_ARG) -> GdUnitSceneRunner:\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\treturn self\n\n\n## Waits for all input events to be processed by flushing any buffered input events\n## and then awaiting a full cycle of both the process and physics frames.[br]\n## [br]\n## This is typically used to ensure that any simulated or queued inputs are fully\n## processed before proceeding with the next steps in the scene.[br]\n## It's essential for reliable input simulation or when synchronizing logic based\n## on inputs.[br]\n##\n## Usage Example:\n## [codeblock]\n## \tawait await_input_processed()  # Ensure all inputs are processed before continuing\n## [/codeblock]\nfunc await_input_processed() -> void:\n\tif scene() != null and scene().process_mode != Node.PROCESS_MODE_DISABLED:\n\t\tInput.flush_buffered_events()\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\tawait (Engine.get_main_loop() as SceneTree).physics_frame\n\n\n## The await_func function pauses execution until a specified function in the scene returns a value.[br]\n## It returns a [GdUnitFuncAssert], which provides a suite of assertion methods to verify the returned value.[br]\n## [member func_name] : The name of the function to wait for.[br]\n## [member args] : Optional function arguments\n## [br]\n## Usage Example:\n## [codeblock]\n## \t# Waits for 'calculate_score' function and verifies the result is equal to 100.\n## \tawait_func(\"calculate_score\").is_equal(100)\n## [/codeblock]\n@warning_ignore(\"unused_parameter\")\nfunc await_func(func_name: String, args := []) -> GdUnitFuncAssert:\n\treturn null\n\n\n\n## The await_func_on function extends the functionality of await_func by allowing you to specify a source node within the scene.[br]\n## It waits for a specified function on that node to return a value and returns a [GdUnitFuncAssert] object for assertions.[br]\n## [member source] : The object where implements the function.[br]\n## [member func_name] : The name of the function to wait for.[br]\n## [member args] : optional function arguments\n## [br]\n## Usage Example:\n## [codeblock]\n## \t# Waits for 'calculate_score' function and verifies the result is equal to 100.\n## \tvar my_instance := ScoreCalculator.new()\n## \tawait_func(my_instance, \"calculate_score\").is_equal(100)\n## [/codeblock]\n@warning_ignore(\"unused_parameter\")\nfunc await_func_on(source: Object, func_name: String, args := []) -> GdUnitFuncAssert:\n\treturn null\n\n\n## Waits for the specified signal to be emitted by the scene. If the signal is not emitted within the given timeout, the operation fails.[br]\n## [member signal_name] : The name of the signal to wait for[br]\n## [member args] : The signal arguments as an array[br]\n## [member timeout] : The maximum duration (in milliseconds) to wait for the signal to be emitted before failing\n@warning_ignore(\"unused_parameter\")\nfunc await_signal(signal_name: String, args := [], timeout := 2000 ) -> void:\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\tpass\n\n\n## Waits for the specified signal to be emitted by a particular source node. If the signal is not emitted within the given timeout, the operation fails.[br]\n## [member source] : the object from which the signal is emitted[br]\n## [member signal_name] : The name of the signal to wait for[br]\n## [member args] : The signal arguments as an array[br]\n## [member timeout] : tThe maximum duration (in milliseconds) to wait for the signal to be emitted before failing\n@warning_ignore(\"unused_parameter\")\nfunc await_signal_on(source: Object, signal_name: String, args := [], timeout := 2000 ) -> void:\n\tpass\n\n\n## Restores the scene window to a windowed mode and brings it to the foreground.[br]\n## This ensures that the scene is visible and active during testing, making it easier to observe and interact with.\nfunc move_window_to_foreground() -> GdUnitSceneRunner:\n\treturn self\n\n\n## Restores the scene window to a windowed mode and brings it to the foreground.[br]\n## This ensures that the scene is visible and active during testing, making it easier to observe and interact with.\n## @deprecated: Use [move_window_to_foreground] instead.\nfunc maximize_view() -> GdUnitSceneRunner:\n\treturn self\n\n\n## Return the current value of the property with the name <name>.[br]\n## [member name] : name of property[br]\n## [member return] : the value of the property\n@warning_ignore(\"unused_parameter\")\nfunc get_property(name: String) -> Variant:\n\treturn null\n\n## Set the  value <value> of the property with the name <name>.[br]\n## [member name] : name of property[br]\n## [member value] : value of property[br]\n## [member return] : true|false depending on valid property name.\n@warning_ignore(\"unused_parameter\")\nfunc set_property(name: String, value: Variant) -> bool:\n\treturn false\n\n\n## executes the function specified by <name> in the scene and returns the result.[br]\n## [member name] : the name of the function to execute[br]\n## [member args] : optional function arguments[br]\n## [member return] : the function result\n@warning_ignore(\"unused_parameter\")\nfunc invoke(\n\tname: String,\n\targ0: Variant = NO_ARG,\n\targ1: Variant = NO_ARG,\n\targ2: Variant = NO_ARG,\n\targ3: Variant = NO_ARG,\n\targ4: Variant = NO_ARG,\n\targ5: Variant = NO_ARG,\n\targ6: Variant = NO_ARG,\n\targ7: Variant = NO_ARG,\n\targ8: Variant = NO_ARG,\n\targ9: Variant = NO_ARG) -> Variant:\n\treturn null\n\n\n## Searches for the specified node with the name in the current scene and returns it, otherwise null.[br]\n## [member name] : the name of the node to find[br]\n## [member recursive] : enables/disables seraching recursive[br]\n## [member return] : the node if find otherwise null\n@warning_ignore(\"unused_parameter\")\nfunc find_child(name: String, recursive: bool = true, owned: bool = false) -> Node:\n\treturn null\n\n\n## Access to current running scene\nfunc scene() -> Node:\n\treturn null\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitSceneRunner.gd.uid",
    "content": "uid://blpb5kd1mhqk4\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitSignalAssert.gd",
    "content": "## An Assertion Tool to verify for emitted signals until a waiting time\nclass_name GdUnitSignalAssert\nextends GdUnitAssert\n\n\n## Verifies that given signal is emitted until waiting time\n@warning_ignore(\"unused_parameter\")\nfunc is_emitted(name :String, args := []) -> GdUnitSignalAssert:\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\treturn self\n\n\n## Verifies that given signal is NOT emitted until waiting time\n@warning_ignore(\"unused_parameter\")\nfunc is_not_emitted(name :String, args := []) -> GdUnitSignalAssert:\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\treturn self\n\n\n## Verifies the signal exists checked the emitter\n@warning_ignore(\"unused_parameter\")\nfunc is_signal_exists(name :String) -> GdUnitSignalAssert:\n\treturn self\n\n\n## Overrides the default failure message by given custom message.\n@warning_ignore(\"unused_parameter\")\nfunc override_failure_message(message :String) -> GdUnitSignalAssert:\n\treturn self\n\n\n## Sets the assert signal timeout in ms, if the time over a failure is reported.[br]\n## e.g.[br]\n## do wait until 5s the instance has emitted the signal `signal_a`[br]\n## [code]assert_signal(instance).wait_until(5000).is_emitted(\"signal_a\")[/code]\n@warning_ignore(\"unused_parameter\")\nfunc wait_until(timeout :int) -> GdUnitSignalAssert:\n\treturn self\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitSignalAssert.gd.uid",
    "content": "uid://bk4kqfkqlxokb\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitStringAssert.gd",
    "content": "## An Assertion Tool to verify String values\nclass_name GdUnitStringAssert\nextends GdUnitAssert\n\n\n## Verifies that the current String is equal to the given one.\n@warning_ignore(\"unused_parameter\")\nfunc is_equal(expected :Variant) -> GdUnitStringAssert:\n\treturn self\n\n\n## Verifies that the current String is equal to the given one, ignoring case considerations.\n@warning_ignore(\"unused_parameter\")\nfunc is_equal_ignoring_case(expected :Variant) -> GdUnitStringAssert:\n\treturn self\n\n\n## Verifies that the current String is not equal to the given one.\n@warning_ignore(\"unused_parameter\")\nfunc is_not_equal(expected :Variant) -> GdUnitStringAssert:\n\treturn self\n\n\n## Verifies that the current String is not equal to the given one, ignoring case considerations.\n@warning_ignore(\"unused_parameter\")\nfunc is_not_equal_ignoring_case(expected :Variant) -> GdUnitStringAssert:\n\treturn self\n\n\n## Verifies that the current String is empty, it has a length of 0.\nfunc is_empty() -> GdUnitStringAssert:\n\treturn self\n\n\n## Verifies that the current String is not empty, it has a length of minimum 1.\nfunc is_not_empty() -> GdUnitStringAssert:\n\treturn self\n\n\n## Verifies that the current String contains the given String.\n@warning_ignore(\"unused_parameter\")\nfunc contains(expected: String) -> GdUnitStringAssert:\n\treturn self\n\n\n## Verifies that the current String does not contain the given String.\n@warning_ignore(\"unused_parameter\")\nfunc not_contains(expected: String) -> GdUnitStringAssert:\n\treturn self\n\n\n## Verifies that the current String does not contain the given String, ignoring case considerations.\n@warning_ignore(\"unused_parameter\")\nfunc contains_ignoring_case(expected: String) -> GdUnitStringAssert:\n\treturn self\n\n\n## Verifies that the current String does not contain the given String, ignoring case considerations.\n@warning_ignore(\"unused_parameter\")\nfunc not_contains_ignoring_case(expected: String) -> GdUnitStringAssert:\n\treturn self\n\n\n## Verifies that the current String starts with the given prefix.\n@warning_ignore(\"unused_parameter\")\nfunc starts_with(expected: String) -> GdUnitStringAssert:\n\treturn self\n\n\n## Verifies that the current String ends with the given suffix.\n@warning_ignore(\"unused_parameter\")\nfunc ends_with(expected: String) -> GdUnitStringAssert:\n\treturn self\n\n\n## Verifies that the current String has the expected length by used comparator.\n@warning_ignore(\"unused_parameter\")\nfunc has_length(length: int, comparator: int = Comparator.EQUAL) -> GdUnitStringAssert:\n\treturn self\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitStringAssert.gd.uid",
    "content": "uid://cwp5ce5us8wek\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitTestSuite.gd",
    "content": "## The main class for all GdUnit test suites[br]\n## This class is the main class to implement your unit tests[br]\n## You have to extend and implement your test cases as described[br]\n## e.g MyTests.gd [br]\n## [codeblock]\n##    extends GdUnitTestSuite\n##    # testcase\n##    func test_case_a():\n##       assert_that(\"value\").is_equal(\"value\")\n## [/codeblock]\n## @tutorial:  https://mikeschulze.github.io/gdUnit4/faq/test-suite/\n\n@icon(\"res://addons/gdUnit4/src/ui/settings/logo.png\")\nclass_name GdUnitTestSuite\nextends Node\n\nconst NO_ARG :Variant = GdUnitConstants.NO_ARG\n\n### internal runtime variables that must not be overwritten!!!\n@warning_ignore(\"unused_private_class_variable\")\nvar __is_skipped := false\n@warning_ignore(\"unused_private_class_variable\")\nvar __skip_reason :String = \"Unknow.\"\nvar __active_test_case :String\nvar __awaiter := __gdunit_awaiter()\n\n\n### We now load all used asserts and tool scripts into the cache according to the principle of \"lazy loading\"\n### in order to noticeably reduce the loading time of the test suite.\n# We go this hard way to increase the loading performance to avoid reparsing all the used scripts\n# for more detailed info -> https://github.com/godotengine/godot/issues/67400\nfunc __lazy_load(script_path :String) -> GDScript:\n\treturn GdUnitAssertions.__lazy_load(script_path)\n\n\nfunc __gdunit_assert() -> GDScript:\n\treturn __lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd\")\n\n\nfunc __gdunit_tools() -> GDScript:\n\treturn __lazy_load(\"res://addons/gdUnit4/src/core/GdUnitTools.gd\")\n\n\nfunc __gdunit_file_access() -> GDScript:\n\treturn __lazy_load(\"res://addons/gdUnit4/src/core/GdUnitFileAccess.gd\")\n\n\nfunc __gdunit_awaiter() -> Object:\n\treturn __lazy_load(\"res://addons/gdUnit4/src/GdUnitAwaiter.gd\").new()\n\n\nfunc __gdunit_argument_matchers() -> GDScript:\n\treturn __lazy_load(\"res://addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd\")\n\n\nfunc __gdunit_object_interactions() -> GDScript:\n\treturn __lazy_load(\"res://addons/gdUnit4/src/core/GdUnitObjectInteractions.gd\")\n\n\n## This function is called before a test suite starts[br]\n## You can overwrite to prepare test data or initalizize necessary variables\nfunc before() -> void:\n\tpass\n\n\n## This function is called at least when a test suite is finished[br]\n## You can overwrite to cleanup data created during test running\nfunc after() -> void:\n\tpass\n\n\n## This function is called before a test case starts[br]\n## You can overwrite to prepare test case specific data\nfunc before_test() -> void:\n\tpass\n\n\n## This function is called after the test case is finished[br]\n## You can overwrite to cleanup your test case specific data\nfunc after_test() -> void:\n\tpass\n\n\nfunc is_failure(_expected_failure :String = NO_ARG) -> bool:\n\treturn Engine.get_meta(\"GD_TEST_FAILURE\") if Engine.has_meta(\"GD_TEST_FAILURE\") else false\n\n\nfunc set_active_test_case(test_case :String) -> void:\n\t__active_test_case = test_case\n\n\n# === Tools ====================================================================\n# Mapps Godot error number to a readable error message. See at ERROR\n# https://docs.godotengine.org/de/stable/classes/class_@globalscope.html#enum-globalscope-error\nfunc error_as_string(error_number :int) -> String:\n\treturn error_string(error_number)\n\n\n## A litle helper to auto freeing your created objects after test execution\nfunc auto_free(obj :Variant) -> Variant:\n\tvar execution_context := GdUnitThreadManager.get_current_context().get_execution_context()\n\n\tassert(execution_context != null, \"INTERNAL ERROR: The current execution_context is null! Please report this as bug.\")\n\treturn execution_context.register_auto_free(obj)\n\n\n@warning_ignore(\"native_method_override\")\nfunc add_child(node :Node, force_readable_name := false, internal := Node.INTERNAL_MODE_DISABLED) -> void:\n\tsuper.add_child(node, force_readable_name, internal)\n\tvar execution_context := GdUnitThreadManager.get_current_context().get_execution_context()\n\tif execution_context != null:\n\t\texecution_context.orphan_monitor_start()\n\n\n## Discard the error message triggered by a timeout (interruption).[br]\n## By default, an interrupted test is reported as an error.[br]\n## This function allows you to change the message to Success when an interrupted error is reported.\nfunc discard_error_interupted_by_timeout() -> void:\n\t@warning_ignore(\"unsafe_method_access\")\n\t__gdunit_tools().register_expect_interupted_by_timeout(self, __active_test_case)\n\n\n## Creates a new directory under the temporary directory *user://tmp*[br]\n## Useful for storing data during test execution. [br]\n## The directory is automatically deleted after test suite execution\nfunc create_temp_dir(relative_path :String) -> String:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_file_access().create_temp_dir(relative_path)\n\n\n## Deletes the temporary base directory[br]\n## Is called automatically after each execution of the test suite\nfunc clean_temp_dir() -> void:\n\t@warning_ignore(\"unsafe_method_access\")\n\t__gdunit_file_access().clear_tmp()\n\n\n## Creates a new file under the temporary directory *user://tmp* + <relative_path>[br]\n## with given name <file_name> and given file <mode> (default = File.WRITE)[br]\n## If success the returned File is automatically closed after the execution of the test suite\nfunc create_temp_file(relative_path :String, file_name :String, mode := FileAccess.WRITE) -> FileAccess:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_file_access().create_temp_file(relative_path, file_name, mode)\n\n\n## Reads a resource by given path <resource_path> into a PackedStringArray.\nfunc resource_as_array(resource_path :String) -> PackedStringArray:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_file_access().resource_as_array(resource_path)\n\n\n## Reads a resource by given path <resource_path> and returned the content as String.\nfunc resource_as_string(resource_path :String) -> String:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_file_access().resource_as_string(resource_path)\n\n\n## Reads a resource by given path <resource_path> and return Variand translated by str_to_var\nfunc resource_as_var(resource_path :String) -> Variant:\n\t@warning_ignore(\"unsafe_method_access\", \"unsafe_cast\")\n\treturn str_to_var(__gdunit_file_access().resource_as_string(resource_path) as String)\n\n\n## Waits for given signal is emited by the <source> until a specified timeout to fail[br]\n## source: the object from which the signal is emitted[br]\n## signal_name: signal name[br]\n## args: the expected signal arguments as an array[br]\n## timeout: the timeout in ms, default is set to 2000ms\nfunc await_signal_on(source :Object, signal_name :String, args :Array = [], timeout :int = 2000) -> Variant:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn await __awaiter.await_signal_on(source, signal_name, args, timeout)\n\n\n## Waits until the next idle frame\nfunc await_idle_frame() -> void:\n\t@warning_ignore(\"unsafe_method_access\")\n\tawait __awaiter.await_idle_frame()\n\n\n## Waits for for a given amount of milliseconds[br]\n## example:[br]\n## [codeblock]\n##    # waits for 100ms\n##    await await_millis(myNode, 100).completed\n## [/codeblock][br]\n## use this waiter and not `await get_tree().create_timer().timeout to prevent errors when a test case is timed out\nfunc await_millis(timeout :int) -> void:\n\t@warning_ignore(\"unsafe_method_access\")\n\tawait __awaiter.await_millis(timeout)\n\n\n## Creates a new scene runner to allow simulate interactions checked a scene.[br]\n## The runner will manage the scene instance and release after the runner is released[br]\n## example:[br]\n## [codeblock]\n##    # creates a runner by using a instanciated scene\n##    var scene = load(\"res://foo/my_scne.tscn\").instantiate()\n##    var runner := scene_runner(scene)\n##\n##    # or simply creates a runner by using the scene resource path\n##    var runner := scene_runner(\"res://foo/my_scne.tscn\")\n## [/codeblock]\nfunc scene_runner(scene :Variant, verbose := false) -> GdUnitSceneRunner:\n\treturn auto_free(__lazy_load(\"res://addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd\").new(scene, verbose))\n\n\n# === Mocking  & Spy ===========================================================\n\n## do return a default value for primitive types or null\nconst RETURN_DEFAULTS = GdUnitMock.RETURN_DEFAULTS\n## do call the real implementation\nconst CALL_REAL_FUNC = GdUnitMock.CALL_REAL_FUNC\n## do return a default value for primitive types and a fully mocked value for Object types\n## builds full deep mocked object\nconst RETURN_DEEP_STUB = GdUnitMock.RETURN_DEEP_STUB\n\n\n## Creates a mock for given class name\nfunc mock(clazz :Variant, mock_mode := RETURN_DEFAULTS) -> Variant:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __lazy_load(\"res://addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd\").build(clazz, mock_mode)\n\n\n## Creates a spy checked given object instance\nfunc spy(instance :Variant) -> Variant:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __lazy_load(\"res://addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd\").build(instance)\n\n\n## Configures a return value for the specified function and used arguments.[br]\n## [b]Example:\n## \t[codeblock]\n## \t\t# overrides the return value of myMock.is_selected() to false\n## \t\tdo_return(false).on(myMock).is_selected()\n## \t[/codeblock]\nfunc do_return(value :Variant) -> GdUnitMock:\n\treturn GdUnitMock.new(value)\n\n\n## Verifies certain behavior happened at least once or exact number of times\nfunc verify(obj :Variant, times := 1) -> Variant:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_object_interactions().verify(obj, times)\n\n\n## Verifies no interactions is happen checked this mock or spy\nfunc verify_no_interactions(obj :Variant) -> GdUnitAssert:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_object_interactions().verify_no_interactions(obj)\n\n\n## Verifies the given mock or spy has any unverified interaction.\nfunc verify_no_more_interactions(obj :Variant) -> GdUnitAssert:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_object_interactions().verify_no_more_interactions(obj)\n\n\n## Resets the saved function call counters checked a mock or spy\nfunc reset(obj :Variant) -> void:\n\t@warning_ignore(\"unsafe_method_access\")\n\t__gdunit_object_interactions().reset(obj)\n\n\n## Starts monitoring the specified source to collect all transmitted signals.[br]\n## The collected signals can then be checked with 'assert_signal'.[br]\n## By default, the specified source is automatically released when the test ends.\n## You can control this behavior by setting auto_free to false if you do not want the source to be automatically freed.[br]\n## Usage:\n##\t[codeblock]\n##\t\tvar emitter := monitor_signals(MyEmitter.new())\n##\t\t# call the function to send the signal\n##\t\temitter.do_it()\n##\t\t# verify the signial is emitted\n##\t\tawait assert_signal(emitter).is_emitted('my_signal')\n##\t[/codeblock]\nfunc monitor_signals(source :Object, _auto_free := true) -> Object:\n\t@warning_ignore(\"unsafe_method_access\")\n\t__lazy_load(\"res://addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd\")\\\n\t\t.get_current_context()\\\n\t\t.get_signal_collector()\\\n\t\t.register_emitter(source)\n\treturn auto_free(source) if _auto_free else source\n\n\n# === Argument matchers ========================================================\n## Argument matcher to match any argument\nfunc any() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().any()\n\n\n## Argument matcher to match any boolean value\nfunc any_bool() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_BOOL)\n\n\n## Argument matcher to match any integer value\nfunc any_int() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_INT)\n\n\n## Argument matcher to match any float value\nfunc any_float() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_FLOAT)\n\n\n## Argument matcher to match any string value\nfunc any_string() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_STRING)\n\n\n## Argument matcher to match any Color value\nfunc any_color() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_COLOR)\n\n\n## Argument matcher to match any Vector typed value\nfunc any_vector() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_types([\n\t\tTYPE_VECTOR2,\n\t\tTYPE_VECTOR2I,\n\t\tTYPE_VECTOR3,\n\t\tTYPE_VECTOR3I,\n\t\tTYPE_VECTOR4,\n\t\tTYPE_VECTOR4I,\n\t])\n\n\n## Argument matcher to match any Vector2 value\nfunc any_vector2() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_VECTOR2)\n\n\n## Argument matcher to match any Vector2i value\nfunc any_vector2i() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_VECTOR2I)\n\n\n## Argument matcher to match any Vector3 value\nfunc any_vector3() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_VECTOR3)\n\n\n## Argument matcher to match any Vector3i value\nfunc any_vector3i() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_VECTOR3I)\n\n\n## Argument matcher to match any Vector4 value\nfunc any_vector4() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_VECTOR4)\n\n\n## Argument matcher to match any Vector3i value\nfunc any_vector4i() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_VECTOR4I)\n\n\n## Argument matcher to match any Rect2 value\nfunc any_rect2() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_RECT2)\n\n\n## Argument matcher to match any Plane value\nfunc any_plane() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_PLANE)\n\n\n## Argument matcher to match any Quaternion value\nfunc any_quat() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_QUATERNION)\n\n\n## Argument matcher to match any AABB value\nfunc any_aabb() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_AABB)\n\n\n## Argument matcher to match any Basis value\nfunc any_basis() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_BASIS)\n\n\n## Argument matcher to match any Transform2D value\nfunc any_transform_2d() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_TRANSFORM2D)\n\n\n## Argument matcher to match any Transform3D value\nfunc any_transform_3d() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_TRANSFORM3D)\n\n\n## Argument matcher to match any NodePath value\nfunc any_node_path() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_NODE_PATH)\n\n\n## Argument matcher to match any RID value\nfunc any_rid() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_RID)\n\n\n## Argument matcher to match any Object value\nfunc any_object() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_OBJECT)\n\n\n## Argument matcher to match any Dictionary value\nfunc any_dictionary() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_DICTIONARY)\n\n\n## Argument matcher to match any Array value\nfunc any_array() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_ARRAY)\n\n\n## Argument matcher to match any PackedByteArray value\nfunc any_packed_byte_array() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_PACKED_BYTE_ARRAY)\n\n\n## Argument matcher to match any PackedInt32Array value\nfunc any_packed_int32_array() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_PACKED_INT32_ARRAY)\n\n\n## Argument matcher to match any PackedInt64Array value\nfunc any_packed_int64_array() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_PACKED_INT64_ARRAY)\n\n\n## Argument matcher to match any PackedFloat32Array value\nfunc any_packed_float32_array() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_PACKED_FLOAT32_ARRAY)\n\n\n## Argument matcher to match any PackedFloat64Array value\nfunc any_packed_float64_array() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_PACKED_FLOAT64_ARRAY)\n\n\n## Argument matcher to match any PackedStringArray value\nfunc any_packed_string_array() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_PACKED_STRING_ARRAY)\n\n\n## Argument matcher to match any PackedVector2Array value\nfunc any_packed_vector2_array() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_PACKED_VECTOR2_ARRAY)\n\n\n## Argument matcher to match any PackedVector3Array value\nfunc any_packed_vector3_array() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_PACKED_VECTOR3_ARRAY)\n\n\n## Argument matcher to match any PackedColorArray value\nfunc any_packed_color_array() -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().by_type(TYPE_PACKED_COLOR_ARRAY)\n\n\n## Argument matcher to match any instance of given class\nfunc any_class(clazz :Object) -> GdUnitArgumentMatcher:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __gdunit_argument_matchers().any_class(clazz)\n\n\n# === value extract utils ======================================================\n## Builds an extractor by given function name and optional arguments\nfunc extr(func_name :String, args := Array()) -> GdUnitValueExtractor:\n\treturn __lazy_load(\"res://addons/gdUnit4/src/extractors/GdUnitFuncValueExtractor.gd\").new(func_name, args)\n\n\n## Constructs a tuple by given arguments\nfunc tuple(arg0 :Variant,\n\targ1 :Variant=NO_ARG,\n\targ2 :Variant=NO_ARG,\n\targ3 :Variant=NO_ARG,\n\targ4 :Variant=NO_ARG,\n\targ5 :Variant=NO_ARG,\n\targ6 :Variant=NO_ARG,\n\targ7 :Variant=NO_ARG,\n\targ8 :Variant=NO_ARG,\n\targ9 :Variant=NO_ARG) -> GdUnitTuple:\n\treturn GdUnitTuple.new(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)\n\n\n# === Asserts ==================================================================\n\n## The common assertion tool to verify values.\n## It checks the given value by type to fit to the best assert\nfunc assert_that(current :Variant) -> GdUnitAssert:\n\tmatch typeof(current):\n\t\tTYPE_BOOL:\n\t\t\treturn assert_bool(current)\n\t\tTYPE_INT:\n\t\t\treturn assert_int(current)\n\t\tTYPE_FLOAT:\n\t\t\treturn assert_float(current)\n\t\tTYPE_STRING:\n\t\t\treturn assert_str(current)\n\t\tTYPE_VECTOR2, TYPE_VECTOR2I, TYPE_VECTOR3, TYPE_VECTOR3I, TYPE_VECTOR4, TYPE_VECTOR4I:\n\t\t\treturn assert_vector(current, false)\n\t\tTYPE_DICTIONARY:\n\t\t\treturn assert_dict(current)\n\t\tTYPE_ARRAY, TYPE_PACKED_BYTE_ARRAY, TYPE_PACKED_INT32_ARRAY, TYPE_PACKED_INT64_ARRAY,\\\n\t\tTYPE_PACKED_FLOAT32_ARRAY, TYPE_PACKED_FLOAT64_ARRAY, TYPE_PACKED_STRING_ARRAY,\\\n\t\tTYPE_PACKED_VECTOR2_ARRAY, TYPE_PACKED_VECTOR3_ARRAY, TYPE_PACKED_COLOR_ARRAY:\n\t\t\treturn assert_array(current, false)\n\t\tTYPE_OBJECT, TYPE_NIL:\n\t\t\treturn assert_object(current)\n\t\t_:\n\t\t\treturn __gdunit_assert().new(current)\n\n\n## An assertion tool to verify boolean values.\nfunc assert_bool(current :Variant) -> GdUnitBoolAssert:\n\treturn __lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd\").new(current)\n\n\n## An assertion tool to verify String values.\nfunc assert_str(current :Variant) -> GdUnitStringAssert:\n\treturn __lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd\").new(current)\n\n\n## An assertion tool to verify integer values.\nfunc assert_int(current :Variant) -> GdUnitIntAssert:\n\treturn __lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd\").new(current)\n\n\n## An assertion tool to verify float values.\nfunc assert_float(current :Variant) -> GdUnitFloatAssert:\n\treturn __lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd\").new(current)\n\n\n## An assertion tool to verify Vector values.[br]\n## This assertion supports all vector types.[br]\n## Usage:\n##     [codeblock]\n##\t\tassert_vector(Vector2(1.2, 1.000001)).is_equal(Vector2(1.2, 1.000001))\n##     [/codeblock]\nfunc assert_vector(current :Variant, type_check := true) -> GdUnitVectorAssert:\n\treturn __lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd\").new(current, type_check)\n\n\n## An assertion tool to verify arrays.\nfunc assert_array(current :Variant, type_check := true) -> GdUnitArrayAssert:\n\treturn __lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd\").new(current, type_check)\n\n\n## An assertion tool to verify dictionaries.\nfunc assert_dict(current :Variant) -> GdUnitDictionaryAssert:\n\treturn __lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd\").new(current)\n\n\n## An assertion tool to verify FileAccess.\nfunc assert_file(current :Variant) -> GdUnitFileAssert:\n\treturn __lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd\").new(current)\n\n\n## An assertion tool to verify Objects.\nfunc assert_object(current :Variant) -> GdUnitObjectAssert:\n\treturn __lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd\").new(current)\n\n\nfunc assert_result(current :Variant) -> GdUnitResultAssert:\n\treturn __lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd\").new(current)\n\n\n## An assertion tool that waits until a certain time for an expected function return value\nfunc assert_func(instance :Object, func_name :String, args := Array()) -> GdUnitFuncAssert:\n\treturn __lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd\").new(instance, func_name, args)\n\n\n## An Assertion Tool to verify for emitted signals until a certain time.\nfunc assert_signal(instance :Object) -> GdUnitSignalAssert:\n\treturn __lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd\").new(instance)\n\n\n## An assertion tool to test for failing assertions.[br]\n## This assert is only designed for internal use to verify failing asserts working as expected.[br]\n## Usage:\n##     [codeblock]\n##\t\tassert_failure(func(): assert_bool(true).is_not_equal(true)) \\\n##\t\t    .has_message(\"Expecting:\\n 'true'\\n not equal to\\n 'true'\")\n##     [/codeblock]\nfunc assert_failure(assertion :Callable) -> GdUnitFailureAssert:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn __lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd\").new().execute(assertion)\n\n\n## An assertion tool to test for failing assertions.[br]\n## This assert is only designed for internal use to verify failing asserts working as expected.[br]\n## Usage:\n##     [codeblock]\n##\t\tawait assert_failure_await(func(): assert_bool(true).is_not_equal(true)) \\\n##\t\t    .has_message(\"Expecting:\\n 'true'\\n not equal to\\n 'true'\")\n##     [/codeblock]\nfunc assert_failure_await(assertion :Callable) -> GdUnitFailureAssert:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn await __lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd\").new().execute_and_await(assertion)\n\n\n## An assertion tool to verify for Godot errors.[br]\n## You can use to verify for certain Godot erros like failing assertions, push_error, push_warn.[br]\n## Usage:\n##     [codeblock]\n##\t\t# tests no error was occured during execution the code\n##\t\tawait assert_error(func (): return 0 )\\\n##\t\t    .is_success()\n##\n##\t\t# tests an push_error('test error') was occured during execution the code\n##\t\tawait assert_error(func (): push_error('test error') )\\\n##\t\t    .is_push_error('test error')\n##     [/codeblock]\nfunc assert_error(current :Callable) -> GdUnitGodotErrorAssert:\n\treturn __lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd\").new(current)\n\n\nfunc assert_not_yet_implemented() -> void:\n\t@warning_ignore(\"unsafe_method_access\")\n\t__gdunit_assert().new(null).do_fail()\n\n\nfunc fail(message :String) -> void:\n\t@warning_ignore(\"unsafe_method_access\")\n\t__gdunit_assert().new(null).report_error(message)\n\n\n# --- internal stuff do not override!!!\nfunc ResourcePath() -> String:\n\treturn get_script().resource_path\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitTestSuite.gd.uid",
    "content": "uid://bbsow5k2y0q4p\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitTuple.gd",
    "content": "## A tuple implementation to hold two or many values\nclass_name GdUnitTuple\nextends RefCounted\n\nconst NO_ARG :Variant = GdUnitConstants.NO_ARG\n\nvar __values :Array = Array()\n\n\nfunc _init(arg0:Variant,\n\targ1 :Variant=NO_ARG,\n\targ2 :Variant=NO_ARG,\n\targ3 :Variant=NO_ARG,\n\targ4 :Variant=NO_ARG,\n\targ5 :Variant=NO_ARG,\n\targ6 :Variant=NO_ARG,\n\targ7 :Variant=NO_ARG,\n\targ8 :Variant=NO_ARG,\n\targ9 :Variant=NO_ARG) -> void:\n\t__values = GdArrayTools.filter_value([arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9], NO_ARG)\n\n\nfunc values() -> Array:\n\treturn __values\n\n\nfunc _to_string() -> String:\n\treturn \"tuple(%s)\" % str(__values)\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitTuple.gd.uid",
    "content": "uid://cwcalyadq4276\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitValueExtractor.gd",
    "content": "## This is the base interface for value extraction\nclass_name GdUnitValueExtractor\nextends RefCounted\n\n\n## Extracts a value by given implementation\nfunc extract_value(value :Variant) -> Variant:\n\tpush_error(\"Uninplemented func 'extract_value'\")\n\treturn value\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitValueExtractor.gd.uid",
    "content": "uid://dd6wo43cc7s5x\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitVectorAssert.gd",
    "content": "## An Assertion Tool to verify Vector values\nclass_name GdUnitVectorAssert\nextends GdUnitAssert\n\n\n## Verifies that the current value is equal to expected one.\n@warning_ignore(\"unused_parameter\")\nfunc is_equal(expected :Variant) -> GdUnitVectorAssert:\n\treturn self\n\n\n## Verifies that the current value is not equal to expected one.\n@warning_ignore(\"unused_parameter\")\nfunc is_not_equal(expected :Variant) -> GdUnitVectorAssert:\n\treturn self\n\n\n## Verifies that the current and expected value are approximately equal.\n@warning_ignore(\"unused_parameter\", \"shadowed_global_identifier\")\nfunc is_equal_approx(expected :Variant, approx :Variant) -> GdUnitVectorAssert:\n\treturn self\n\n\n## Verifies that the current value is less than the given one.\n@warning_ignore(\"unused_parameter\")\nfunc is_less(expected :Variant) -> GdUnitVectorAssert:\n\treturn self\n\n\n## Verifies that the current value is less than or equal the given one.\n@warning_ignore(\"unused_parameter\")\nfunc is_less_equal(expected :Variant) -> GdUnitVectorAssert:\n\treturn self\n\n\n## Verifies that the current value is greater than the given one.\n@warning_ignore(\"unused_parameter\")\nfunc is_greater(expected :Variant) -> GdUnitVectorAssert:\n\treturn self\n\n\n## Verifies that the current value is greater than or equal the given one.\n@warning_ignore(\"unused_parameter\")\nfunc is_greater_equal(expected :Variant) -> GdUnitVectorAssert:\n\treturn self\n\n\n## Verifies that the current value is between the given boundaries (inclusive).\n@warning_ignore(\"unused_parameter\")\nfunc is_between(from :Variant, to :Variant) -> GdUnitVectorAssert:\n\treturn self\n\n\n## Verifies that the current value is not between the given boundaries (inclusive).\n@warning_ignore(\"unused_parameter\")\nfunc is_not_between(from :Variant, to :Variant) -> GdUnitVectorAssert:\n\treturn self\n"
  },
  {
    "path": "addons/gdUnit4/src/GdUnitVectorAssert.gd.uid",
    "content": "uid://d0jl2n5k6xvfn\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/CallBackValueProvider.gd",
    "content": "# a value provider unsing a callback to get `next` value from a certain function\nclass_name CallBackValueProvider\nextends ValueProvider\n\nvar _cb :Callable\nvar _args :Array\n\n\nfunc _init(instance :Object, func_name :String, args :Array = Array(), force_error := true) -> void:\n\t_cb = Callable(instance, func_name);\n\t_args = args\n\tif force_error and not _cb.is_valid():\n\t\tpush_error(\"Can't find function '%s' checked instance %s\" % [func_name, instance])\n\n\nfunc get_value() -> Variant:\n\tif not _cb.is_valid():\n\t\treturn null\n\tif _args.is_empty():\n\t\treturn await _cb.call()\n\treturn await _cb.callv(_args)\n\n\nfunc dispose() -> void:\n\t_cb = Callable()\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/CallBackValueProvider.gd.uid",
    "content": "uid://djwh83x3xq1qa\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/DefaultValueProvider.gd",
    "content": "# default value provider, simple returns the initial value\nclass_name DefaultValueProvider\nextends ValueProvider\n\nvar _value: Variant\n\n\nfunc _init(value: Variant) -> void:\n\t_value = value\n\n\nfunc get_value() -> Variant:\n\treturn _value\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/DefaultValueProvider.gd.uid",
    "content": "uid://b0an8wdbprin0\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdAssertMessages.gd",
    "content": "class_name GdAssertMessages\nextends Resource\n\nconst WARN_COLOR = \"#EFF883\"\nconst ERROR_COLOR = \"#CD5C5C\"\nconst VALUE_COLOR = \"#1E90FF\"\nconst SUB_COLOR :=  Color(1, 0, 0, .3)\nconst ADD_COLOR :=  Color(0, 1, 0, .3)\n\n\nstatic func format_dict(value :Variant) -> String:\n\tif not value is Dictionary:\n\t\treturn str(value)\n\n\tvar dict_value: Dictionary = value\n\tif dict_value.is_empty():\n\t\treturn \"{ }\"\n\tvar as_rows := var_to_str(value).split(\"\\n\")\n\tfor index in range( 1, as_rows.size()-1):\n\t\tas_rows[index] = \"\t\" + as_rows[index]\n\tas_rows[-1] = \"  \" + as_rows[-1]\n\treturn \"\\n\".join(as_rows)\n\n\n# improved version of InputEvent as text\nstatic func input_event_as_text(event :InputEvent) -> String:\n\tvar text := \"\"\n\tif event is InputEventKey:\n\t\tvar key_event := event as InputEventKey\n\t\ttext += \"InputEventKey : key='%s', pressed=%s, keycode=%d, physical_keycode=%s\" % [\n\t\t\t\t\tevent.as_text(), key_event.pressed, key_event.keycode, key_event.physical_keycode]\n\telse:\n\t\ttext += event.as_text()\n\tif event is InputEventMouse:\n\t\tvar mouse_event := event as InputEventMouse\n\t\ttext += \", global_position %s\" % mouse_event.global_position\n\tif event is InputEventWithModifiers:\n\t\tvar mouse_event := event as InputEventWithModifiers\n\t\ttext += \", shift=%s, alt=%s, control=%s, meta=%s, command=%s\" % [\n\t\t\t\t\tmouse_event.shift_pressed,\n\t\t\t\t\tmouse_event.alt_pressed,\n\t\t\t\t\tmouse_event.ctrl_pressed,\n\t\t\t\t\tmouse_event.meta_pressed,\n\t\t\t\t\tmouse_event.command_or_control_autoremap]\n\treturn text\n\n\nstatic func _colored_string_div(characters :String) -> String:\n\treturn colored_array_div(characters.to_utf8_buffer())\n\n\nstatic func colored_array_div(characters :PackedByteArray) -> String:\n\tif characters.is_empty():\n\t\treturn \"<empty>\"\n\tvar result := PackedByteArray()\n\tvar index := 0\n\tvar missing_chars := PackedByteArray()\n\tvar additional_chars := PackedByteArray()\n\n\twhile index < characters.size():\n\t\tvar character := characters[index]\n\t\tmatch character:\n\t\t\tGdDiffTool.DIV_ADD:\n\t\t\t\tindex += 1\n\t\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t\tadditional_chars.append(characters[index])\n\t\t\tGdDiffTool.DIV_SUB:\n\t\t\t\tindex += 1\n\t\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t\tmissing_chars.append(characters[index])\n\t\t\t_:\n\t\t\t\tif not missing_chars.is_empty():\n\t\t\t\t\tresult.append_array(format_chars(missing_chars, SUB_COLOR))\n\t\t\t\t\tmissing_chars = PackedByteArray()\n\t\t\t\tif not additional_chars.is_empty():\n\t\t\t\t\tresult.append_array(format_chars(additional_chars, ADD_COLOR))\n\t\t\t\t\tadditional_chars = PackedByteArray()\n\t\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t\tresult.append(character)\n\t\tindex += 1\n\n\tresult.append_array(format_chars(missing_chars, SUB_COLOR))\n\tresult.append_array(format_chars(additional_chars, ADD_COLOR))\n\treturn result.get_string_from_utf8()\n\n\nstatic func _typed_value(value :Variant) -> String:\n\treturn GdDefaultValueDecoder.decode(value)\n\n\nstatic func _warning(error :String) -> String:\n\treturn \"[color=%s]%s[/color]\" % [WARN_COLOR, error]\n\n\nstatic func _error(error :String) -> String:\n\treturn \"[color=%s]%s[/color]\" % [ERROR_COLOR, error]\n\n\nstatic func _nerror(number :Variant) -> String:\n\tmatch typeof(number):\n\t\tTYPE_INT:\n\t\t\treturn \"[color=%s]%d[/color]\" % [ERROR_COLOR, number]\n\t\tTYPE_FLOAT:\n\t\t\treturn \"[color=%s]%f[/color]\" % [ERROR_COLOR, number]\n\t\t_:\n\t\t\treturn \"[color=%s]%s[/color]\" % [ERROR_COLOR, str(number)]\n\n\nstatic func _colored_value(value :Variant) -> String:\n\tmatch typeof(value):\n\t\tTYPE_STRING, TYPE_STRING_NAME:\n\t\t\treturn \"'[color=%s]%s[/color]'\" % [VALUE_COLOR, _colored_string_div(str(value))]\n\t\tTYPE_INT:\n\t\t\treturn \"'[color=%s]%d[/color]'\" % [VALUE_COLOR, value]\n\t\tTYPE_FLOAT:\n\t\t\treturn \"'[color=%s]%s[/color]'\" % [VALUE_COLOR, _typed_value(value)]\n\t\tTYPE_COLOR:\n\t\t\treturn \"'[color=%s]%s[/color]'\" % [VALUE_COLOR, _typed_value(value)]\n\t\tTYPE_OBJECT:\n\t\t\tif value == null:\n\t\t\t\treturn \"'[color=%s]<null>[/color]'\" % [VALUE_COLOR]\n\t\t\tif value is InputEvent:\n\t\t\t\tvar ie: InputEvent = value\n\t\t\t\treturn \"[color=%s]<%s>[/color]\" % [VALUE_COLOR, input_event_as_text(ie)]\n\t\t\tvar obj_value: Object = value\n\t\t\tif obj_value.has_method(\"_to_string\"):\n\t\t\t\treturn \"[color=%s]<%s>[/color]\" % [VALUE_COLOR, str(value)]\n\t\t\treturn \"[color=%s]<%s>[/color]\" % [VALUE_COLOR, obj_value.get_class()]\n\t\tTYPE_DICTIONARY:\n\t\t\treturn \"'[color=%s]%s[/color]'\" % [VALUE_COLOR, format_dict(value)]\n\t\t_:\n\t\t\tif GdArrayTools.is_array_type(value):\n\t\t\t\treturn \"'[color=%s]%s[/color]'\" % [VALUE_COLOR, _typed_value(value)]\n\t\t\treturn \"'[color=%s]%s[/color]'\" % [VALUE_COLOR, value]\n\n\n\nstatic func _index_report_as_table(index_reports :Array) -> String:\n\tvar table := \"[table=3]$cells[/table]\"\n\tvar header := \"[cell][right][b]$text[/b][/right]\\t[/cell]\"\n\tvar cell := \"[cell][right]$text[/right]\\t[/cell]\"\n\tvar cells := header.replace(\"$text\", \"Index\") + header.replace(\"$text\", \"Current\") + header.replace(\"$text\", \"Expected\")\n\tfor report :Variant in index_reports:\n\t\tvar index :String = str(report[\"index\"])\n\t\tvar current :String = str(report[\"current\"])\n\t\tvar expected :String = str(report[\"expected\"])\n\t\tcells += cell.replace(\"$text\", index) + cell.replace(\"$text\", current) + cell.replace(\"$text\", expected)\n\treturn table.replace(\"$cells\", cells)\n\n\nstatic func orphan_detected_on_suite_setup(count :int) -> String:\n\treturn \"%s\\n Detected <%d> orphan nodes during test suite setup stage! [b]Check before() and after()![/b]\" % [\n\t\t_warning(\"WARNING:\"), count]\n\n\nstatic func orphan_detected_on_test_setup(count :int) -> String:\n\treturn \"%s\\n Detected <%d> orphan nodes during test setup! [b]Check before_test() and after_test()![/b]\" % [\n\t\t_warning(\"WARNING:\"), count]\n\n\nstatic func orphan_detected_on_test(count :int) -> String:\n\treturn \"%s\\n Detected <%d> orphan nodes during test execution!\" % [\n\t\t_warning(\"WARNING:\"), count]\n\n\nstatic func fuzzer_interuped(iterations: int, error: String) -> String:\n\treturn \"%s %s %s\\n %s\" % [\n\t\t_error(\"Found an error after\"),\n\t\t_colored_value(iterations + 1),\n\t\t_error(\"test iterations\"),\n\t\terror]\n\n\nstatic func test_timeout(timeout :int) -> String:\n\treturn \"%s\\n %s\" % [_error(\"Timeout !\"), _colored_value(\"Test timed out after %s\" %  LocalTime.elapsed(timeout))]\n\n\n# gdlint:disable = mixed-tabs-and-spaces\nstatic func test_suite_skipped(hint :String, skip_count :int) -> String:\n\treturn \"\"\"\n\t\t%s\n\t\t  Skipped %s tests\n\t\t  Reason: %s\n\t\t\"\"\".dedent().trim_prefix(\"\\n\")\\\n\t\t% [_error(\"The Entire test-suite is skipped!\"), _colored_value(skip_count), _colored_value(hint)]\n\n\nstatic func test_skipped(hint :String) -> String:\n\treturn \"\"\"\n\t\t%s\n\t\t  Reason: %s\n\t\t\"\"\".dedent().trim_prefix(\"\\n\")\\\n\t\t% [_error(\"This test is skipped!\"), _colored_value(hint)]\n\n\nstatic func error_not_implemented() -> String:\n\treturn _error(\"Test not implemented!\")\n\n\nstatic func error_is_null(current :Variant) -> String:\n\treturn \"%s %s but was %s\" % [_error(\"Expecting:\"), _colored_value(null), _colored_value(current)]\n\n\nstatic func error_is_not_null() -> String:\n\treturn \"%s %s\" % [_error(\"Expecting: not to be\"), _colored_value(null)]\n\n\nstatic func error_equal(current :Variant, expected :Variant, index_reports :Array = []) -> String:\n\tvar report := \"\"\"\n\t\t%s\n\t\t %s\n\t\t but was\n\t\t %s\"\"\".dedent().trim_prefix(\"\\n\") % [_error(\"Expecting:\"), _colored_value(expected), _colored_value(current)]\n\tif not index_reports.is_empty():\n\t\treport += \"\\n\\n%s\\n%s\" % [_error(\"Differences found:\"), _index_report_as_table(index_reports)]\n\treturn report\n\n\nstatic func error_not_equal(current :Variant, expected :Variant) -> String:\n\treturn \"%s\\n %s\\n not equal to\\n %s\" % [_error(\"Expecting:\"), _colored_value(expected), _colored_value(current)]\n\n\nstatic func error_not_equal_case_insensetiv(current :Variant, expected :Variant) -> String:\n\treturn \"%s\\n %s\\n not equal to (case insensitiv)\\n %s\" % [\n\t\t\t_error(\"Expecting:\"), _colored_value(expected), _colored_value(current)]\n\n\nstatic func error_is_empty(current :Variant) -> String:\n\treturn \"%s\\n must be empty but was\\n %s\" % [_error(\"Expecting:\"), _colored_value(current)]\n\n\nstatic func error_is_not_empty() -> String:\n\treturn \"%s\\n must not be empty\" % [_error(\"Expecting:\")]\n\n\nstatic func error_is_same(current :Variant, expected :Variant) -> String:\n\treturn \"%s\\n %s\\n to refer to the same object\\n %s\" % [_error(\"Expecting:\"), _colored_value(expected), _colored_value(current)]\n\n\n@warning_ignore(\"unused_parameter\")\nstatic func error_not_same(_current :Variant, expected :Variant) -> String:\n\treturn \"%s\\n %s\" % [_error(\"Expecting not same:\"), _colored_value(expected)]\n\n\nstatic func error_not_same_error(current :Variant, expected :Variant) -> String:\n\treturn \"%s\\n %s\\n but was\\n %s\" % [_error(\"Expecting error message:\"), _colored_value(expected), _colored_value(current)]\n\n\nstatic func error_is_instanceof(current: GdUnitResult, expected :GdUnitResult) -> String:\n\treturn \"%s\\n %s\\n But it was %s\" % [_error(\"Expected instance of:\"),\\\n\t\t_colored_value(expected.or_else(null)), _colored_value(current.or_else(null))]\n\n\n# -- Boolean Assert specific messages -----------------------------------------------------\nstatic func error_is_true(current :Variant) -> String:\n\treturn \"%s %s but is %s\" % [_error(\"Expecting:\"), _colored_value(true), _colored_value(current)]\n\n\nstatic func error_is_false(current :Variant) -> String:\n\treturn \"%s %s but is %s\" % [_error(\"Expecting:\"), _colored_value(false), _colored_value(current)]\n\n\n# - Integer/Float Assert specific messages -----------------------------------------------------\n\nstatic func error_is_even(current :Variant) -> String:\n\treturn \"%s\\n %s must be even\" % [_error(\"Expecting:\"), _colored_value(current)]\n\n\nstatic func error_is_odd(current :Variant) -> String:\n\treturn \"%s\\n %s must be odd\" % [_error(\"Expecting:\"), _colored_value(current)]\n\n\nstatic func error_is_negative(current :Variant) -> String:\n\treturn \"%s\\n %s be negative\" % [_error(\"Expecting:\"), _colored_value(current)]\n\n\nstatic func error_is_not_negative(current :Variant) -> String:\n\treturn \"%s\\n %s be not negative\" % [_error(\"Expecting:\"), _colored_value(current)]\n\n\nstatic func error_is_zero(current :Variant) -> String:\n\treturn \"%s\\n equal to 0 but is %s\" % [_error(\"Expecting:\"), _colored_value(current)]\n\n\nstatic func error_is_not_zero() -> String:\n\treturn \"%s\\n not equal to 0\" % [_error(\"Expecting:\")]\n\n\nstatic func error_is_wrong_type(current_type :Variant.Type, expected_type :Variant.Type) -> String:\n\treturn \"%s\\n Expecting type %s but is %s\" % [\n\t\t_error(\"Unexpected type comparison:\"),\n\t\t_colored_value(GdObjects.type_as_string(current_type)),\n\t\t_colored_value(GdObjects.type_as_string(expected_type))]\n\n\nstatic func error_is_value(operation :int, current :Variant, expected :Variant, expected2 :Variant = null) -> String:\n\tmatch operation:\n\t\tComparator.EQUAL:\n\t\t\treturn \"%s\\n %s but was '%s'\" % [_error(\"Expecting:\"), _colored_value(expected), _nerror(current)]\n\t\tComparator.LESS_THAN:\n\t\t\treturn \"%s\\n %s but was '%s'\" % [_error(\"Expecting to be less than:\"), _colored_value(expected), _nerror(current)]\n\t\tComparator.LESS_EQUAL:\n\t\t\treturn \"%s\\n %s but was '%s'\" % [_error(\"Expecting to be less than or equal:\"), _colored_value(expected), _nerror(current)]\n\t\tComparator.GREATER_THAN:\n\t\t\treturn \"%s\\n %s but was '%s'\" % [_error(\"Expecting to be greater than:\"), _colored_value(expected), _nerror(current)]\n\t\tComparator.GREATER_EQUAL:\n\t\t\treturn \"%s\\n %s but was '%s'\" % [_error(\"Expecting to be greater than or equal:\"), _colored_value(expected), _nerror(current)]\n\t\tComparator.BETWEEN_EQUAL:\n\t\t\treturn \"%s\\n %s\\n in range between\\n %s <> %s\" % [\n\t\t\t\t\t_error(\"Expecting:\"), _colored_value(current), _colored_value(expected), _colored_value(expected2)]\n\t\tComparator.NOT_BETWEEN_EQUAL:\n\t\t\treturn \"%s\\n %s\\n not in range between\\n %s <> %s\" % [\n\t\t\t\t\t_error(\"Expecting:\"), _colored_value(current), _colored_value(expected), _colored_value(expected2)]\n\treturn \"TODO create expected message\"\n\n\nstatic func error_is_in(current :Variant, expected :Array) -> String:\n\treturn \"%s\\n %s\\n is in\\n %s\" % [_error(\"Expecting:\"), _colored_value(current), _colored_value(str(expected))]\n\n\nstatic func error_is_not_in(current :Variant, expected :Array) -> String:\n\treturn \"%s\\n %s\\n is not in\\n %s\" % [_error(\"Expecting:\"), _colored_value(current), _colored_value(str(expected))]\n\n\n# - StringAssert ---------------------------------------------------------------------------------\nstatic func error_equal_ignoring_case(current :Variant, expected :Variant) -> String:\n\treturn \"%s\\n %s\\n but was\\n %s (ignoring case)\" % [_error(\"Expecting:\"), _colored_value(expected), _colored_value(current)]\n\n\nstatic func error_contains(current :Variant, expected :Variant) -> String:\n\treturn \"%s\\n %s\\n do contains\\n %s\" % [_error(\"Expecting:\"), _colored_value(current), _colored_value(expected)]\n\n\nstatic func error_not_contains(current :Variant, expected :Variant) -> String:\n\treturn \"%s\\n %s\\n not do contain\\n %s\" % [_error(\"Expecting:\"), _colored_value(current), _colored_value(expected)]\n\n\nstatic func error_contains_ignoring_case(current :Variant, expected :Variant) -> String:\n\treturn \"%s\\n %s\\n contains\\n %s\\n (ignoring case)\" % [_error(\"Expecting:\"), _colored_value(current), _colored_value(expected)]\n\n\nstatic func error_not_contains_ignoring_case(current :Variant, expected :Variant) -> String:\n\treturn \"%s\\n %s\\n not do contains\\n %s\\n (ignoring case)\" % [_error(\"Expecting:\"), _colored_value(current), _colored_value(expected)]\n\n\nstatic func error_starts_with(current :Variant, expected :Variant) -> String:\n\treturn \"%s\\n %s\\n to start with\\n %s\" % [_error(\"Expecting:\"), _colored_value(current), _colored_value(expected)]\n\n\nstatic func error_ends_with(current :Variant, expected :Variant) -> String:\n\treturn \"%s\\n %s\\n to end with\\n %s\" % [_error(\"Expecting:\"), _colored_value(current), _colored_value(expected)]\n\n\nstatic func error_has_length(current :Variant, expected: int, compare_operator :int) -> String:\n\t@warning_ignore(\"unsafe_method_access\")\n\tvar current_length :Variant = current.length() if current != null else null\n\tmatch compare_operator:\n\t\tComparator.EQUAL:\n\t\t\treturn \"%s\\n %s but was '%s' in\\n %s\" % [\n\t\t\t\t\t_error(\"Expecting size:\"), _colored_value(expected), _nerror(current_length), _colored_value(current)]\n\t\tComparator.LESS_THAN:\n\t\t\treturn \"%s\\n %s but was '%s' in\\n %s\" % [\n\t\t\t\t\t_error(\"Expecting size to be less than:\"), _colored_value(expected), _nerror(current_length), _colored_value(current)]\n\t\tComparator.LESS_EQUAL:\n\t\t\treturn \"%s\\n %s but was '%s' in\\n %s\" % [\n\t\t\t\t\t_error(\"Expecting size to be less than or equal:\"), _colored_value(expected),\n\t\t\t\t\t_nerror(current_length), _colored_value(current)]\n\t\tComparator.GREATER_THAN:\n\t\t\treturn \"%s\\n %s but was '%s' in\\n %s\" % [\n\t\t\t\t\t_error(\"Expecting size to be greater than:\"), _colored_value(expected),\n\t\t\t\t\t_nerror(current_length), _colored_value(current)]\n\t\tComparator.GREATER_EQUAL:\n\t\t\treturn \"%s\\n %s but was '%s' in\\n %s\" % [\n\t\t\t\t\t_error(\"Expecting size to be greater than or equal:\"), _colored_value(expected),\n\t\t\t\t\t_nerror(current_length), _colored_value(current)]\n\treturn \"TODO create expected message\"\n\n\n# - ArrayAssert specific messgaes ---------------------------------------------------\n\nstatic func error_arr_contains(current: Variant, expected: Variant, not_expect: Variant, not_found: Variant, by_reference: bool) -> String:\n\tvar failure_message := \"Expecting contains SAME elements:\" if by_reference else \"Expecting contains elements:\"\n\tvar error := \"%s\\n %s\\n do contains (in any order)\\n %s\" % [\n\t\t\t\t\t_error(failure_message), _colored_value(current), _colored_value(expected)]\n\tif not is_empty(not_expect):\n\t\terror += \"\\nbut some elements where not expected:\\n %s\" % _colored_value(not_expect)\n\tif not is_empty(not_found):\n\t\tvar prefix := \"but\" if is_empty(not_expect) else \"and\"\n\t\terror += \"\\n%s could not find elements:\\n %s\" % [prefix, _colored_value(not_found)]\n\treturn error\n\n\nstatic func error_arr_contains_exactly(\n\tcurrent: Variant,\n\texpected: Variant,\n\tnot_expect: Variant,\n\tnot_found: Variant, compare_mode: GdObjects.COMPARE_MODE) -> String:\n\tvar failure_message := (\n\t\t\"Expecting contains exactly elements:\" if compare_mode == GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST\n\t\telse \"Expecting contains SAME exactly elements:\"\n\t)\n\tif is_empty(not_expect) and is_empty(not_found):\n\t\tvar arr_current: Array = current\n\t\tvar arr_expected: Array = expected\n\t\tvar diff := _find_first_diff(arr_current, arr_expected)\n\t\treturn \"%s\\n %s\\n do contains (in same order)\\n %s\\n but has different order %s\"  % [\n\t\t\t\t\t_error(failure_message), _colored_value(current), _colored_value(expected), diff]\n\n\tvar error := \"%s\\n %s\\n do contains (in same order)\\n %s\" % [\n\t\t\t\t\t_error(failure_message), _colored_value(current), _colored_value(expected)]\n\tif not is_empty(not_expect):\n\t\terror += \"\\nbut some elements where not expected:\\n %s\" % _colored_value(not_expect)\n\tif not is_empty(not_found):\n\t\tvar prefix := \"but\" if is_empty(not_expect) else \"and\"\n\t\terror += \"\\n%s could not find elements:\\n %s\" % [prefix, _colored_value(not_found)]\n\treturn error\n\n\nstatic func error_arr_contains_exactly_in_any_order(\n\tcurrent: Variant,\n\texpected: Variant,\n\tnot_expect: Variant,\n\tnot_found: Variant,\n\tcompare_mode: GdObjects.COMPARE_MODE) -> String:\n\n\tvar failure_message := (\n\t\t\"Expecting contains exactly elements:\" if compare_mode == GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST\n\t\telse \"Expecting contains SAME exactly elements:\"\n\t)\n\tvar error := \"%s\\n %s\\n do contains exactly (in any order)\\n %s\" % [\n\t\t\t\t\t_error(failure_message), _colored_value(current), _colored_value(expected)]\n\tif not is_empty(not_expect):\n\t\terror += \"\\nbut some elements where not expected:\\n %s\" % _colored_value(not_expect)\n\tif not is_empty(not_found):\n\t\tvar prefix := \"but\" if is_empty(not_expect) else \"and\"\n\t\terror += \"\\n%s could not find elements:\\n %s\" % [prefix, _colored_value(not_found)]\n\treturn error\n\n\nstatic func error_arr_not_contains(current: Variant, expected: Variant, found: Variant, compare_mode: GdObjects.COMPARE_MODE) -> String:\n\tvar failure_message := \"Expecting:\" if compare_mode == GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST else \"Expecting SAME:\"\n\tvar error := \"%s\\n %s\\n do not contains\\n %s\" % [\n\t\t\t\t\t_error(failure_message), _colored_value(current), _colored_value(expected)]\n\tif not is_empty(found):\n\t\terror += \"\\n but found elements:\\n %s\" % _colored_value(found)\n\treturn error\n\n\n# - DictionaryAssert specific messages ----------------------------------------------\nstatic func error_contains_keys(current :Array, expected :Array, keys_not_found :Array, compare_mode :GdObjects.COMPARE_MODE) -> String:\n\tvar failure := (\n\t\t\"Expecting contains keys:\" if compare_mode == GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST\n\t\telse \"Expecting contains SAME keys:\"\n\t)\n\treturn \"%s\\n %s\\n to contains:\\n %s\\n but can't find key's:\\n %s\" % [\n\t\t\t_error(failure), _colored_value(current), _colored_value(expected), _colored_value(keys_not_found)]\n\n\nstatic func error_not_contains_keys(current :Array, expected :Array, keys_not_found :Array, compare_mode :GdObjects.COMPARE_MODE) -> String:\n\tvar failure := (\n\t\t\"Expecting NOT contains keys:\" if compare_mode == GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST\n\t\telse \"Expecting NOT contains SAME keys\"\n\t)\n\treturn \"%s\\n %s\\n do not contains:\\n %s\\n but contains key's:\\n %s\" % [\n\t\t\t_error(failure), _colored_value(current), _colored_value(expected), _colored_value(keys_not_found)]\n\n\nstatic func error_contains_key_value(key :Variant, value :Variant, current_value :Variant, compare_mode :GdObjects.COMPARE_MODE) -> String:\n\tvar failure := (\n\t\t\"Expecting contains key and value:\" if compare_mode == GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST\n\t\telse \"Expecting contains SAME key and value:\"\n\t)\n\treturn \"%s\\n %s : %s\\n but contains\\n %s : %s\" % [\n\t\t\t_error(failure), _colored_value(key), _colored_value(value), _colored_value(key), _colored_value(current_value)]\n\n\n# - ResultAssert specific errors ----------------------------------------------------\nstatic func error_result_is_empty(current :GdUnitResult) -> String:\n\treturn _result_error_message(current, GdUnitResult.EMPTY)\n\n\nstatic func error_result_is_success(current :GdUnitResult) -> String:\n\treturn _result_error_message(current, GdUnitResult.SUCCESS)\n\n\nstatic func error_result_is_warning(current :GdUnitResult) -> String:\n\treturn _result_error_message(current, GdUnitResult.WARN)\n\n\nstatic func error_result_is_error(current :GdUnitResult) -> String:\n\treturn _result_error_message(current, GdUnitResult.ERROR)\n\n\nstatic func error_result_has_message(current :String, expected :String) -> String:\n\treturn \"%s\\n %s\\n but was\\n %s.\" % [_error(\"Expecting:\"), _colored_value(expected), _colored_value(current)]\n\n\nstatic func error_result_has_message_on_success(expected :String) -> String:\n\treturn \"%s\\n %s\\n but the GdUnitResult is a success.\" % [_error(\"Expecting:\"), _colored_value(expected)]\n\n\nstatic func error_result_is_value(current :Variant, expected :Variant) -> String:\n\treturn \"%s\\n %s\\n but was\\n %s.\" % [_error(\"Expecting to contain same value:\"), _colored_value(expected), _colored_value(current)]\n\n\nstatic func _result_error_message(current :GdUnitResult, expected_type :int) -> String:\n\tif current == null:\n\t\treturn _error(\"Expecting the result must be a %s but was <null>.\" % result_type(expected_type))\n\tif current.is_success():\n\t\treturn _error(\"Expecting the result must be a %s but was SUCCESS.\" % result_type(expected_type))\n\tvar error := \"Expecting the result must be a %s but was %s:\" % [result_type(expected_type), result_type(current._state)]\n\treturn \"%s\\n %s\" % [_error(error), _colored_value(result_message(current))]\n\n\nstatic func error_interrupted(func_name :String, expected :Variant, elapsed :String) -> String:\n\tfunc_name = humanized(func_name)\n\tif expected == null:\n\t\treturn \"%s %s but timed out after %s\" % [_error(\"Expected:\"), func_name, elapsed]\n\treturn \"%s %s %s but timed out after %s\" % [_error(\"Expected:\"), func_name, _colored_value(expected), elapsed]\n\n\nstatic func error_wait_signal(signal_name :String, args :Array, elapsed :String) -> String:\n\tif args.is_empty():\n\t\treturn \"%s %s but timed out after %s\" % [\n\t\t\t\t_error(\"Expecting emit signal:\"), _colored_value(signal_name + \"()\"), elapsed]\n\treturn \"%s %s but timed out after %s\" % [\n\t\t\t_error(\"Expecting emit signal:\"), _colored_value(signal_name + \"(\" + str(args) + \")\"), elapsed]\n\n\nstatic func error_signal_emitted(signal_name :String, args :Array, elapsed :String) -> String:\n\tif args.is_empty():\n\t\treturn \"%s %s but is emitted after %s\" % [\n\t\t\t\t_error(\"Expecting do not emit signal:\"), _colored_value(signal_name + \"()\"), elapsed]\n\treturn \"%s %s but is emitted after %s\" % [\n\t\t\t_error(\"Expecting do not emit signal:\"), _colored_value(signal_name + \"(\" + str(args) + \")\"), elapsed]\n\n\nstatic func error_await_signal_on_invalid_instance(source :Variant, signal_name :String, args :Array) -> String:\n\treturn \"%s\\n await_signal_on(%s, %s, %s)\" % [\n\t\t\t_error(\"Invalid source! Can't await on signal:\"), _colored_value(source), signal_name, args]\n\n\nstatic func result_type(type :int) -> String:\n\tmatch type:\n\t\tGdUnitResult.SUCCESS: return \"SUCCESS\"\n\t\tGdUnitResult.WARN: return \"WARNING\"\n\t\tGdUnitResult.ERROR: return \"ERROR\"\n\t\tGdUnitResult.EMPTY: return \"EMPTY\"\n\treturn \"UNKNOWN\"\n\n\nstatic func result_message(result :GdUnitResult) -> String:\n\tmatch result._state:\n\t\tGdUnitResult.SUCCESS: return \"\"\n\t\tGdUnitResult.WARN: return result.warn_message()\n\t\tGdUnitResult.ERROR: return result.error_message()\n\t\tGdUnitResult.EMPTY: return \"\"\n\treturn \"UNKNOWN\"\n# -----------------------------------------------------------------------------------\n\n# - Spy|Mock specific errors ----------------------------------------------------\nstatic func error_no_more_interactions(summary :Dictionary) -> String:\n\tvar interactions := PackedStringArray()\n\tfor args :Array in summary.keys():\n\t\tvar times :int = summary[args]\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tinteractions.append(_format_arguments(args, times))\n\treturn \"%s\\n%s\\n%s\" % [_error(\"Expecting no more interactions!\"), _error(\"But found interactions on:\"), \"\\n\".join(interactions)]\n\n\nstatic func error_validate_interactions(current_interactions: Dictionary, expected_interactions: Dictionary) -> String:\n\tvar collected_interactions := PackedStringArray()\n\tfor args: Array in current_interactions.keys():\n\t\tvar times: int = current_interactions[args]\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tcollected_interactions.append(_format_arguments(args, times))\n\n\tvar arguments: Array = expected_interactions.keys()[0]\n\tvar interactions: int = expected_interactions.values()[0]\n\tvar expected_interaction := _format_arguments(arguments, interactions)\n\treturn \"%s\\n%s\\n%s\\n%s\" % [\n\t\t\t_error(\"Expecting interaction on:\"), expected_interaction, _error(\"But found interactions on:\"), \"\\n\".join(collected_interactions)]\n\n\nstatic func _format_arguments(args :Array, times :int) -> String:\n\tvar fname :String = args[0]\n\tvar fargs := args.slice(1) as Array\n\tvar typed_args := _to_typed_args(fargs)\n\tvar fsignature := _colored_value(\"%s(%s)\" % [fname, \", \".join(typed_args)])\n\treturn \"\t%s\t%d time's\" % [fsignature, times]\n\n\nstatic func _to_typed_args(args :Array) -> PackedStringArray:\n\tvar typed := PackedStringArray()\n\tfor arg :Variant in args:\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\ttyped.append(_format_arg(arg) + \" :\" + GdObjects.type_as_string(typeof(arg)))\n\treturn typed\n\n\nstatic func _format_arg(arg :Variant) -> String:\n\tif arg is InputEvent:\n\t\tvar ie: InputEvent = arg\n\t\treturn input_event_as_text(ie)\n\treturn str(arg)\n\n\nstatic func _find_first_diff(left :Array, right :Array) -> String:\n\tfor index in left.size():\n\t\tvar l :Variant = left[index]\n\t\tvar r :Variant = \"<no entry>\" if index >= right.size() else right[index]\n\t\tif not GdObjects.equals(l, r):\n\t\t\treturn \"at position %s\\n '%s' vs '%s'\" % [_colored_value(index), _typed_value(l), _typed_value(r)]\n\treturn \"\"\n\n\nstatic func error_has_size(current :Variant, expected: int) -> String:\n\t@warning_ignore(\"unsafe_method_access\")\n\tvar current_size :Variant = null if current == null else current.size()\n\treturn \"%s\\n %s\\n but was\\n %s\" % [_error(\"Expecting size:\"), _colored_value(expected), _colored_value(current_size)]\n\n\nstatic func error_contains_exactly(current: Array, expected: Array) -> String:\n\treturn \"%s\\n %s\\n but was\\n %s\" % [_error(\"Expecting exactly equal:\"), _colored_value(expected), _colored_value(current)]\n\n\nstatic func format_chars(characters :PackedByteArray, type :Color) -> PackedByteArray:\n\tif characters.size() == 0:# or characters[0] == 10:\n\t\treturn characters\n\tvar result := PackedByteArray()\n\tvar message := \"[bgcolor=#%s][color=with]%s[/color][/bgcolor]\" % [\n\t\t\t\t\ttype.to_html(), characters.get_string_from_utf8().replace(\"\\n\", \"<LF>\")]\n\tresult.append_array(message.to_utf8_buffer())\n\treturn result\n\n\nstatic func format_invalid(value :String) -> String:\n\treturn \"[bgcolor=#%s][color=with]%s[/color][/bgcolor]\" % [SUB_COLOR.to_html(), value]\n\n\nstatic func humanized(value :String) -> String:\n\treturn value.replace(\"_\", \" \")\n\n\nstatic func build_failure_message(failure :String, additional_failure_message: String, custom_failure_message: String) -> String:\n\tvar message := failure if custom_failure_message.is_empty() else custom_failure_message\n\tif additional_failure_message.is_empty():\n\t\treturn message\n\treturn \"\"\"\n\t\t%s\n\t\t[color=LIME_GREEN][b]Additional info:[/b][/color]\n\t\t %s\"\"\".dedent().trim_prefix(\"\\n\") % [message, additional_failure_message]\n\n\nstatic func is_empty(value: Variant) -> bool:\n\tvar arry_value: Array = value\n\treturn arry_value != null and arry_value.is_empty()\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdAssertMessages.gd.uid",
    "content": "uid://c0t4vvxotq3bh\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdAssertReports.gd",
    "content": "class_name GdAssertReports\nextends RefCounted\n\nconst LAST_ERROR = \"last_assert_error_message\"\nconst LAST_ERROR_LINE = \"last_assert_error_line\"\n\n\nstatic func report_success() -> void:\n\tGdUnitSignals.instance().gdunit_set_test_failed.emit(false)\n\tGdAssertReports.set_last_error_line_number(-1)\n\tEngine.remove_meta(LAST_ERROR)\n\n\nstatic func report_warning(message :String, line_number :int) -> void:\n\tGdUnitSignals.instance().gdunit_set_test_failed.emit(false)\n\tsend_report(GdUnitReport.new().create(GdUnitReport.WARN, line_number, message))\n\n\nstatic func report_error(message:String, line_number :int) -> void:\n\tGdUnitSignals.instance().gdunit_set_test_failed.emit(true)\n\tGdAssertReports.set_last_error_line_number(line_number)\n\tEngine.set_meta(LAST_ERROR, message)\n\t# if we expect to fail we handle as success test\n\tif _do_expect_assert_failing():\n\t\treturn\n\tsend_report(GdUnitReport.new().create(GdUnitReport.FAILURE, line_number, message))\n\n\nstatic func reset_last_error_line_number() -> void:\n\tEngine.remove_meta(LAST_ERROR_LINE)\n\n\nstatic func set_last_error_line_number(line_number :int) -> void:\n\tEngine.set_meta(LAST_ERROR_LINE, line_number)\n\n\nstatic func get_last_error_line_number() -> int:\n\tif Engine.has_meta(LAST_ERROR_LINE):\n\t\treturn Engine.get_meta(LAST_ERROR_LINE)\n\treturn -1\n\n\nstatic func _do_expect_assert_failing() -> bool:\n\tif Engine.has_meta(GdUnitConstants.EXPECT_ASSERT_REPORT_FAILURES):\n\t\treturn Engine.get_meta(GdUnitConstants.EXPECT_ASSERT_REPORT_FAILURES)\n\treturn false\n\n\nstatic func current_failure() -> String:\n\treturn Engine.get_meta(LAST_ERROR)\n\n\nstatic func send_report(report :GdUnitReport) -> void:\n\tGdUnitThreadManager.get_current_context().get_execution_context().add_report(report)\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdAssertReports.gd.uid",
    "content": "uid://7iaiuyltmrq4\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd",
    "content": "class_name GdUnitArrayAssertImpl\nextends GdUnitArrayAssert\n\n\nvar _base: GdUnitAssertImpl\nvar _current_value_provider: ValueProvider\nvar _type_check: bool\n\n\nfunc _init(current: Variant, type_check := true) -> void:\n\t_type_check = type_check\n\t_current_value_provider = DefaultValueProvider.new(current)\n\t_base = GdUnitAssertImpl.new(current)\n\t# save the actual assert instance on the current thread context\n\tGdUnitThreadManager.get_current_context().set_assert(self)\n\tif not _validate_value_type(current):\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\treport_error(\"GdUnitArrayAssert inital error, unexpected type <%s>\" % GdObjects.typeof_as_string(current))\n\n\nfunc _notification(event: int) -> void:\n\tif event == NOTIFICATION_PREDELETE:\n\t\tif _base != null:\n\t\t\t_base.notification(event)\n\t\t\t_base = null\n\n\nfunc report_success() -> GdUnitArrayAssert:\n\t_base.report_success()\n\treturn self\n\n\nfunc report_error(error: String) -> GdUnitArrayAssert:\n\t_base.report_error(error)\n\treturn self\n\n\nfunc failure_message() -> String:\n\treturn _base.failure_message()\n\n\nfunc override_failure_message(message: String) -> GdUnitArrayAssert:\n\t_base.override_failure_message(message)\n\treturn self\n\n\nfunc append_failure_message(message: String) -> GdUnitArrayAssert:\n\t_base.append_failure_message(message)\n\treturn self\n\n\nfunc _validate_value_type(value: Variant) -> bool:\n\treturn value == null or GdArrayTools.is_array_type(value)\n\n\nfunc get_current_value() -> Variant:\n\treturn _current_value_provider.get_value()\n\n\nfunc max_length(left: Variant, right: Variant) -> int:\n\tvar ls := str(left).length()\n\tvar rs := str(right).length()\n\treturn rs if ls < rs else ls\n\n\n# gdlint: disable=function-name\nfunc _toPackedStringArray(value: Variant) -> PackedStringArray:\n\tif GdArrayTools.is_array_type(value):\n\t\t@warning_ignore(\"unsafe_cast\")\n\t\treturn PackedStringArray(value as Array)\n\treturn PackedStringArray([str(value)])\n\n\nfunc _array_equals_div(current: Variant, expected: Variant, case_sensitive: bool = false) -> Array[Array]:\n\tvar current_value := _toPackedStringArray(current)\n\tvar expected_value := _toPackedStringArray(expected)\n\tvar index_report := Array()\n\tfor index in current_value.size():\n\t\tvar c := current_value[index]\n\t\tif index < expected_value.size():\n\t\t\tvar e := expected_value[index]\n\t\t\tif not GdObjects.equals(c, e, case_sensitive):\n\t\t\t\tvar length := max_length(c, e)\n\t\t\t\tcurrent_value[index] = GdAssertMessages.format_invalid(c.lpad(length))\n\t\t\t\texpected_value[index] = e.lpad(length)\n\t\t\t\tindex_report.push_back({\"index\": index, \"current\": c, \"expected\": e})\n\t\telse:\n\t\t\tcurrent_value[index] = GdAssertMessages.format_invalid(c)\n\t\t\tindex_report.push_back({\"index\": index, \"current\": c, \"expected\": \"<N/A>\"})\n\n\tfor index in range(current_value.size(), expected_value.size()):\n\t\tvar value := expected_value[index]\n\t\texpected_value[index] = GdAssertMessages.format_invalid(value)\n\t\tindex_report.push_back({\"index\": index, \"current\": \"<N/A>\", \"expected\": value})\n\treturn [current_value, expected_value, index_report]\n\n\nfunc _array_div(compare_mode: GdObjects.COMPARE_MODE, left: Array[Variant], right: Array[Variant], _same_order := false) -> Array[Variant]:\n\tvar not_expect := left.duplicate(true)\n\tvar not_found := right.duplicate(true)\n\tfor index_c in left.size():\n\t\tvar c: Variant = left[index_c]\n\t\tfor index_e in right.size():\n\t\t\tvar e: Variant = right[index_e]\n\t\t\tif GdObjects.equals(c, e, false, compare_mode):\n\t\t\t\tGdArrayTools.erase_value(not_expect, e)\n\t\t\t\tGdArrayTools.erase_value(not_found, c)\n\t\t\t\tbreak\n\treturn [not_expect, not_found]\n\n\nfunc _contains(expected: Variant, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert:\n\tif not _validate_value_type(expected):\n\t\treturn report_error(\"ERROR: expected value: <%s>\\n is not a Array Type!\" % GdObjects.typeof_as_string(expected))\n\tvar by_reference := compare_mode == GdObjects.COMPARE_MODE.OBJECT_REFERENCE\n\tvar current_value: Variant = get_current_value()\n\tif current_value == null:\n\t\treturn report_error(GdAssertMessages.error_arr_contains(current_value, expected, [], expected, by_reference))\n\t@warning_ignore(\"unsafe_cast\")\n\tvar diffs := _array_div(compare_mode, current_value as Array[Variant], expected as Array[Variant])\n\t#var not_expect := diffs[0] as Array\n\tvar not_found: Array = diffs[1]\n\tif not not_found.is_empty():\n\t\treturn report_error(GdAssertMessages.error_arr_contains(current_value, expected, [], not_found, by_reference))\n\treturn report_success()\n\n\nfunc _contains_exactly(expected: Variant, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert:\n\tif not _validate_value_type(expected):\n\t\treturn report_error(\"ERROR: expected value: <%s>\\n is not a Array Type!\" % GdObjects.typeof_as_string(expected))\n\tvar current_value: Variant = get_current_value()\n\tif current_value == null:\n\t\treturn report_error(GdAssertMessages.error_arr_contains_exactly(null, expected, [], expected, compare_mode))\n\t# has same content in same order\n\tif _is_equal(current_value, expected, false, compare_mode):\n\t\treturn report_success()\n\t# check has same elements but in different order\n\tif _is_equals_sorted(current_value, expected, false, compare_mode):\n\t\treturn report_error(GdAssertMessages.error_arr_contains_exactly(current_value, expected, [], [], compare_mode))\n\t# find the difference\n\t@warning_ignore(\"unsafe_cast\")\n\tvar diffs := _array_div(compare_mode,\n\t\tcurrent_value as Array[Variant],\n\t\texpected as Array[Variant],\n\t\tGdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST)\n\tvar not_expect: Array[Variant] = diffs[0]\n\tvar not_found: Array[Variant] = diffs[1]\n\treturn report_error(GdAssertMessages.error_arr_contains_exactly(current_value, expected, not_expect, not_found, compare_mode))\n\n\nfunc _contains_exactly_in_any_order(expected: Variant, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert:\n\tif not _validate_value_type(expected):\n\t\treturn report_error(\"ERROR: expected value: <%s>\\n is not a Array Type!\" % GdObjects.typeof_as_string(expected))\n\tvar current_value: Variant = get_current_value()\n\tif current_value == null:\n\t\treturn report_error(GdAssertMessages.error_arr_contains_exactly_in_any_order(current_value, expected, [], expected, compare_mode))\n\t# find the difference\n\t@warning_ignore(\"unsafe_cast\")\n\tvar diffs := _array_div(compare_mode, current_value as Array[Variant], expected as Array[Variant], false)\n\tvar not_expect: Array[Variant] = diffs[0]\n\tvar not_found: Array[Variant] = diffs[1]\n\tif not_expect.is_empty() and not_found.is_empty():\n\t\treturn report_success()\n\treturn report_error(GdAssertMessages.error_arr_contains_exactly_in_any_order(current_value, expected, not_expect, not_found, compare_mode))\n\n\nfunc _not_contains(expected: Variant, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert:\n\tif not _validate_value_type(expected):\n\t\treturn report_error(\"ERROR: expected value: <%s>\\n is not a Array Type!\" % GdObjects.typeof_as_string(expected))\n\tvar current_value: Variant = get_current_value()\n\tif current_value == null:\n\t\treturn report_error(GdAssertMessages.error_arr_contains_exactly_in_any_order(current_value, expected, [], expected, compare_mode))\n\t@warning_ignore(\"unsafe_cast\")\n\tvar diffs := _array_div(compare_mode, current_value as Array[Variant], expected as Array[Variant])\n\tvar found: Array[Variant] = diffs[0]\n\t@warning_ignore(\"unsafe_cast\")\n\tif found.size() == (current_value as Array).size():\n\t\treturn report_success()\n\t@warning_ignore(\"unsafe_cast\")\n\tvar diffs2 := _array_div(compare_mode, expected as Array[Variant], diffs[1] as Array[Variant])\n\treturn report_error(GdAssertMessages.error_arr_not_contains(current_value, expected, diffs2[0], compare_mode))\n\n\nfunc is_null() -> GdUnitArrayAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_null()\n\treturn self\n\n\nfunc is_not_null() -> GdUnitArrayAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_not_null()\n\treturn self\n\n\n# Verifies that the current String is equal to the given one.\nfunc is_equal(expected: Variant) -> GdUnitArrayAssert:\n\tif _type_check and not _validate_value_type(expected):\n\t\treturn report_error(\"ERROR: expected value: <%s>\\n is not a Array Type!\" % GdObjects.typeof_as_string(expected))\n\tvar current_value: Variant = get_current_value()\n\tif current_value == null and expected != null:\n\t\treturn report_error(GdAssertMessages.error_equal(null, expected))\n\tif not _is_equal(current_value, expected):\n\t\tvar diff := _array_equals_div(current_value, expected)\n\t\tvar expected_as_list := GdArrayTools.as_string(diff[0], false)\n\t\tvar current_as_list := GdArrayTools.as_string(diff[1], false)\n\t\tvar index_report: Array = diff[2]\n\t\treturn report_error(GdAssertMessages.error_equal(expected_as_list, current_as_list, index_report))\n\treturn report_success()\n\n\n# Verifies that the current Array is equal to the given one, ignoring case considerations.\nfunc is_equal_ignoring_case(expected: Variant) -> GdUnitArrayAssert:\n\tif _type_check and not _validate_value_type(expected):\n\t\treturn report_error(\"ERROR: expected value: <%s>\\n is not a Array Type!\" % GdObjects.typeof_as_string(expected))\n\tvar current_value: Variant = get_current_value()\n\tif current_value == null and expected != null:\n\t\t@warning_ignore(\"unsafe_cast\")\n\t\treturn report_error(GdAssertMessages.error_equal(null, GdArrayTools.as_string(expected as Array)))\n\tif not _is_equal(current_value, expected, true):\n\t\t@warning_ignore(\"unsafe_cast\")\n\t\tvar diff := _array_equals_div(current_value as Array[Variant], expected as Array[Variant], true)\n\t\tvar expected_as_list := GdArrayTools.as_string(diff[0])\n\t\tvar current_as_list := GdArrayTools.as_string(diff[1])\n\t\tvar index_report: Array = diff[2]\n\t\treturn report_error(GdAssertMessages.error_equal(expected_as_list, current_as_list, index_report))\n\treturn report_success()\n\n\nfunc is_not_equal(expected: Variant) -> GdUnitArrayAssert:\n\tif not _validate_value_type(expected):\n\t\treturn report_error(\"ERROR: expected value: <%s>\\n is not a Array Type!\" % GdObjects.typeof_as_string(expected))\n\tvar current_value: Variant = get_current_value()\n\tif _is_equal(current_value, expected):\n\t\treturn report_error(GdAssertMessages.error_not_equal(current_value, expected))\n\treturn report_success()\n\n\nfunc is_not_equal_ignoring_case(expected: Variant) -> GdUnitArrayAssert:\n\tif not _validate_value_type(expected):\n\t\treturn report_error(\"ERROR: expected value: <%s>\\n is not a Array Type!\" % GdObjects.typeof_as_string(expected))\n\tvar current_value: Variant = get_current_value()\n\tif _is_equal(current_value, expected, true):\n\t\t@warning_ignore(\"unsafe_cast\")\n\t\tvar c := GdArrayTools.as_string(current_value as Array)\n\t\t@warning_ignore(\"unsafe_cast\")\n\t\tvar e := GdArrayTools.as_string(expected as Array)\n\t\treturn report_error(GdAssertMessages.error_not_equal_case_insensetiv(c, e))\n\treturn report_success()\n\n\nfunc is_empty() -> GdUnitArrayAssert:\n\tvar current_value: Variant = get_current_value()\n\t@warning_ignore(\"unsafe_cast\")\n\tif current_value == null or (current_value as Array).size() > 0:\n\t\treturn report_error(GdAssertMessages.error_is_empty(current_value))\n\treturn report_success()\n\n\nfunc is_not_empty() -> GdUnitArrayAssert:\n\tvar current_value: Variant = get_current_value()\n\t@warning_ignore(\"unsafe_cast\")\n\tif current_value != null and (current_value as Array).size() == 0:\n\t\treturn report_error(GdAssertMessages.error_is_not_empty())\n\treturn report_success()\n\n\n@warning_ignore(\"unused_parameter\", \"shadowed_global_identifier\")\nfunc is_same(expected: Variant) -> GdUnitArrayAssert:\n\tif not _validate_value_type(expected):\n\t\treturn report_error(\"ERROR: expected value: <%s>\\n is not a Array Type!\" % GdObjects.typeof_as_string(expected))\n\tvar current: Variant = get_current_value()\n\tif not is_same(current, expected):\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\treport_error(GdAssertMessages.error_is_same(current, expected))\n\treturn self\n\n\nfunc is_not_same(expected: Variant) -> GdUnitArrayAssert:\n\tif not _validate_value_type(expected):\n\t\treturn report_error(\"ERROR: expected value: <%s>\\n is not a Array Type!\" % GdObjects.typeof_as_string(expected))\n\tvar current: Variant = get_current_value()\n\tif is_same(current, expected):\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\treport_error(GdAssertMessages.error_not_same(current, expected))\n\treturn self\n\n\nfunc has_size(expected: int) -> GdUnitArrayAssert:\n\tvar current_value: Variant = get_current_value()\n\t@warning_ignore(\"unsafe_cast\")\n\tif current_value == null or (current_value as Array).size() != expected:\n\t\treturn report_error(GdAssertMessages.error_has_size(current_value, expected))\n\treturn report_success()\n\n\nfunc contains(expected: Variant) -> GdUnitArrayAssert:\n\treturn _contains(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST)\n\n\nfunc contains_exactly(expected: Variant) -> GdUnitArrayAssert:\n\treturn _contains_exactly(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST)\n\n\nfunc contains_exactly_in_any_order(expected: Variant) -> GdUnitArrayAssert:\n\treturn _contains_exactly_in_any_order(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST)\n\n\nfunc contains_same(expected: Variant) -> GdUnitArrayAssert:\n\treturn _contains(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE)\n\n\nfunc contains_same_exactly(expected: Variant) -> GdUnitArrayAssert:\n\treturn _contains_exactly(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE)\n\n\nfunc contains_same_exactly_in_any_order(expected: Variant) -> GdUnitArrayAssert:\n\treturn _contains_exactly_in_any_order(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE)\n\n\nfunc not_contains(expected: Variant) -> GdUnitArrayAssert:\n\treturn _not_contains(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST)\n\n\nfunc not_contains_same(expected: Variant) -> GdUnitArrayAssert:\n\treturn _not_contains(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE)\n\n\nfunc is_instanceof(expected: Variant) -> GdUnitAssert:\n\t@warning_ignore(\"unsafe_method_access\")\n\t_base.is_instanceof(expected)\n\treturn self\n\n\nfunc extract(func_name: String, args := Array()) -> GdUnitArrayAssert:\n\tvar extracted_elements := Array()\n\n\tvar extractor := GdUnitFuncValueExtractor.new(func_name, args)\n\tvar current: Variant = get_current_value()\n\tif current == null:\n\t\t_current_value_provider = DefaultValueProvider.new(null)\n\telse:\n\t\tfor element: Variant in current:\n\t\t\textracted_elements.append(extractor.extract_value(element))\n\t\t_current_value_provider = DefaultValueProvider.new(extracted_elements)\n\treturn self\n\n\nfunc extractv(\n\textr0: GdUnitValueExtractor,\n\textr1: GdUnitValueExtractor = null,\n\textr2: GdUnitValueExtractor = null,\n\textr3: GdUnitValueExtractor = null,\n\textr4: GdUnitValueExtractor = null,\n\textr5: GdUnitValueExtractor = null,\n\textr6: GdUnitValueExtractor = null,\n\textr7: GdUnitValueExtractor = null,\n\textr8: GdUnitValueExtractor = null,\n\textr9: GdUnitValueExtractor = null) -> GdUnitArrayAssert:\n\tvar extractors: Variant = GdArrayTools.filter_value([extr0, extr1, extr2, extr3, extr4, extr5, extr6, extr7, extr8, extr9], null)\n\tvar extracted_elements := Array()\n\tvar current: Variant = get_current_value()\n\tif current == null:\n\t\t_current_value_provider = DefaultValueProvider.new(null)\n\telse:\n\t\tfor element: Variant in current:\n\t\t\tvar ev: Array[Variant] = [\n\t\t\t\tGdUnitTuple.NO_ARG,\n\t\t\t\tGdUnitTuple.NO_ARG,\n\t\t\t\tGdUnitTuple.NO_ARG,\n\t\t\t\tGdUnitTuple.NO_ARG,\n\t\t\t\tGdUnitTuple.NO_ARG,\n\t\t\t\tGdUnitTuple.NO_ARG,\n\t\t\t\tGdUnitTuple.NO_ARG,\n\t\t\t\tGdUnitTuple.NO_ARG,\n\t\t\t\tGdUnitTuple.NO_ARG,\n\t\t\t\tGdUnitTuple.NO_ARG\n\t\t\t]\n\t\t\t@warning_ignore(\"unsafe_cast\")\n\t\t\tfor index: int in (extractors as Array).size():\n\t\t\t\tvar extractor: GdUnitValueExtractor = extractors[index]\n\t\t\t\tev[index] = extractor.extract_value(element)\n\t\t\t@warning_ignore(\"unsafe_cast\")\n\t\t\tif (extractors as Array).size() > 1:\n\t\t\t\textracted_elements.append(GdUnitTuple.new(ev[0], ev[1], ev[2], ev[3], ev[4], ev[5], ev[6], ev[7], ev[8], ev[9]))\n\t\t\telse:\n\t\t\t\textracted_elements.append(ev[0])\n\t\t_current_value_provider = DefaultValueProvider.new(extracted_elements)\n\treturn self\n\n\n@warning_ignore(\"incompatible_ternary\")\nfunc _is_equal(\n\tleft: Variant,\n\tright: Variant,\n\tcase_sensitive := false,\n\tcompare_mode := GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST) -> bool:\n\n\t@warning_ignore(\"unsafe_cast\")\n\treturn GdObjects.equals(\n\t\t(left as Array) if GdArrayTools.is_array_type(left) else left,\n\t\t(right as Array) if GdArrayTools.is_array_type(right) else right,\n\t\tcase_sensitive,\n\t\tcompare_mode\n\t)\n\n\nfunc _is_equals_sorted(\n\tleft: Variant,\n\tright: Variant,\n\tcase_sensitive := false,\n\tcompare_mode := GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST) -> bool:\n\n\t@warning_ignore(\"unsafe_cast\")\n\treturn GdObjects.equals_sorted(\n\t\tleft as Array,\n\t\tright as Array,\n\t\tcase_sensitive,\n\t\tcompare_mode)\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd.uid",
    "content": "uid://ws7gw7v5ooho\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd",
    "content": "class_name GdUnitAssertImpl\nextends GdUnitAssert\n\n\nvar _current :Variant\nvar _current_failure_message :String = \"\"\nvar _custom_failure_message :String = \"\"\nvar _additional_failure_message: String = \"\"\n\n\nfunc _init(current :Variant) -> void:\n\t_current = current\n\t# save the actual assert instance on the current thread context\n\tGdUnitThreadManager.get_current_context().set_assert(self)\n\tGdAssertReports.reset_last_error_line_number()\n\n\n\nfunc failure_message() -> String:\n\treturn _current_failure_message\n\n\nfunc current_value() -> Variant:\n\treturn _current\n\n\nfunc report_success() -> GdUnitAssert:\n\tGdAssertReports.report_success()\n\treturn self\n\n\nfunc report_error(failure :String, failure_line_number: int = -1) -> GdUnitAssert:\n\tvar line_number := failure_line_number if failure_line_number != -1 else GdUnitAssertions.get_line_number()\n\tGdAssertReports.set_last_error_line_number(line_number)\n\t_current_failure_message = GdAssertMessages.build_failure_message(failure, _additional_failure_message, _custom_failure_message)\n\tGdAssertReports.report_error(_current_failure_message, line_number)\n\tEngine.set_meta(\"GD_TEST_FAILURE\", true)\n\treturn self\n\n\nfunc do_fail() -> GdUnitAssert:\n\treturn report_error(GdAssertMessages.error_not_implemented())\n\n\nfunc override_failure_message(message :String) -> GdUnitAssert:\n\t_custom_failure_message = message\n\treturn self\n\n\nfunc append_failure_message(message :String) -> GdUnitAssert:\n\t_additional_failure_message = message\n\treturn self\n\n\nfunc is_equal(expected :Variant) -> GdUnitAssert:\n\tvar current :Variant = current_value()\n\tif not GdObjects.equals(current, expected):\n\t\treturn report_error(GdAssertMessages.error_equal(current, expected))\n\treturn report_success()\n\n\nfunc is_not_equal(expected :Variant) -> GdUnitAssert:\n\tvar current :Variant = current_value()\n\tif GdObjects.equals(current, expected):\n\t\treturn report_error(GdAssertMessages.error_not_equal(current, expected))\n\treturn report_success()\n\n\nfunc is_null() -> GdUnitAssert:\n\tvar current :Variant = current_value()\n\tif current != null:\n\t\treturn report_error(GdAssertMessages.error_is_null(current))\n\treturn report_success()\n\n\nfunc is_not_null() -> GdUnitAssert:\n\tvar current :Variant = current_value()\n\tif current == null:\n\t\treturn report_error(GdAssertMessages.error_is_not_null())\n\treturn report_success()\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd.uid",
    "content": "uid://degy1ximnodhd\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitAssertions.gd",
    "content": "# Preloads all GdUnit assertions\nclass_name GdUnitAssertions\nextends RefCounted\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc _init() -> void:\n\t# preload all gdunit assertions to speedup testsuite loading time\n\t# gdlint:disable=private-method-call\n\tGdUnitAssertions.__lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd\")\n\tGdUnitAssertions.__lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd\")\n\tGdUnitAssertions.__lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd\")\n\tGdUnitAssertions.__lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd\")\n\tGdUnitAssertions.__lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd\")\n\tGdUnitAssertions.__lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd\")\n\tGdUnitAssertions.__lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd\")\n\tGdUnitAssertions.__lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd\")\n\tGdUnitAssertions.__lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd\")\n\tGdUnitAssertions.__lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd\")\n\tGdUnitAssertions.__lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd\")\n\tGdUnitAssertions.__lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd\")\n\tGdUnitAssertions.__lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd\")\n\tGdUnitAssertions.__lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd\")\n\tGdUnitAssertions.__lazy_load(\"res://addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd\")\n\n\n### We now load all used asserts and tool scripts into the cache according to the principle of \"lazy loading\"\n### in order to noticeably reduce the loading time of the test suite.\n# We go this hard way to increase the loading performance to avoid reparsing all the used scripts\n# for more detailed info -> https://github.com/godotengine/godot/issues/67400\n# gdlint:disable=function-name\nstatic func __lazy_load(script_path :String) -> GDScript:\n\treturn ResourceLoader.load(script_path, \"GDScript\", ResourceLoader.CACHE_MODE_REUSE)\n\n\nstatic func validate_value_type(value :Variant, type :Variant.Type) -> bool:\n\treturn value == null or typeof(value) == type\n\n\n# Scans the current stack trace for the root cause to extract the line number\nstatic func get_line_number() -> int:\n\tvar stack_trace := get_stack()\n\tif stack_trace == null or stack_trace.is_empty():\n\t\treturn -1\n\tfor index in stack_trace.size():\n\t\tvar stack_info :Dictionary = stack_trace[index]\n\t\tvar function :String = stack_info.get(\"function\")\n\t\t# we catch helper asserts to skip over to return the correct line number\n\t\tif function.begins_with(\"assert_\"):\n\t\t\tcontinue\n\t\tif function.begins_with(\"test_\"):\n\t\t\treturn stack_info.get(\"line\")\n\t\tvar source :String = stack_info.get(\"source\")\n\t\tif source.is_empty() \\\n\t\t\tor source.begins_with(\"user://\") \\\n\t\t\tor source.ends_with(\"GdUnitAssert.gd\") \\\n\t\t\tor source.ends_with(\"GdUnitAssertions.gd\") \\\n\t\t\tor source.ends_with(\"AssertImpl.gd\") \\\n\t\t\tor source.ends_with(\"GdUnitTestSuite.gd\") \\\n\t\t\tor source.ends_with(\"GdUnitSceneRunnerImpl.gd\") \\\n\t\t\tor source.ends_with(\"GdUnitObjectInteractions.gd\") \\\n\t\t\tor source.ends_with(\"GdUnitAwaiter.gd\"):\n\t\t\tcontinue\n\t\treturn stack_info.get(\"line\")\n\treturn -1\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitAssertions.gd.uid",
    "content": "uid://dvvf5ab6g1340\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd",
    "content": "extends GdUnitBoolAssert\n\nvar _base: GdUnitAssertImpl\n\n\nfunc _init(current :Variant) -> void:\n\t_base = GdUnitAssertImpl.new(current)\n\t# save the actual assert instance on the current thread context\n\tGdUnitThreadManager.get_current_context().set_assert(self)\n\tif not GdUnitAssertions.validate_value_type(current, TYPE_BOOL):\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\treport_error(\"GdUnitBoolAssert inital error, unexpected type <%s>\" % GdObjects.typeof_as_string(current))\n\n\nfunc _notification(event :int) -> void:\n\tif event == NOTIFICATION_PREDELETE:\n\t\tif _base != null:\n\t\t\t_base.notification(event)\n\t\t\t_base = null\n\n\nfunc current_value() -> Variant:\n\treturn _base.current_value()\n\n\nfunc report_success() -> GdUnitBoolAssert:\n\t_base.report_success()\n\treturn self\n\n\nfunc report_error(error :String) -> GdUnitBoolAssert:\n\t_base.report_error(error)\n\treturn self\n\n\nfunc failure_message() -> String:\n\treturn _base.failure_message()\n\n\nfunc override_failure_message(message :String) -> GdUnitBoolAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.override_failure_message(message)\n\treturn self\n\n\nfunc append_failure_message(message :String) -> GdUnitBoolAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.append_failure_message(message)\n\treturn self\n\n\n# Verifies that the current value is null.\nfunc is_null() -> GdUnitBoolAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_null()\n\treturn self\n\n\n# Verifies that the current value is not null.\nfunc is_not_null() -> GdUnitBoolAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_not_null()\n\treturn self\n\n\nfunc is_equal(expected: Variant) -> GdUnitBoolAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_equal(expected)\n\treturn self\n\n\nfunc is_not_equal(expected: Variant) -> GdUnitBoolAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_not_equal(expected)\n\treturn self\n\n\nfunc is_true() -> GdUnitBoolAssert:\n\tif current_value() != true:\n\t\treturn report_error(GdAssertMessages.error_is_true(current_value()))\n\treturn report_success()\n\n\nfunc is_false() -> GdUnitBoolAssert:\n\tif current_value() == true || current_value() == null:\n\t\treturn report_error(GdAssertMessages.error_is_false(current_value()))\n\treturn report_success()\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd.uid",
    "content": "uid://cpjmy6h7cmpug\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd",
    "content": "extends GdUnitDictionaryAssert\n\nvar _base: GdUnitAssertImpl\n\n\nfunc _init(current :Variant) -> void:\n\t_base = GdUnitAssertImpl.new(current)\n\t# save the actual assert instance on the current thread context\n\tGdUnitThreadManager.get_current_context().set_assert(self)\n\tif not GdUnitAssertions.validate_value_type(current, TYPE_DICTIONARY):\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\treport_error(\"GdUnitDictionaryAssert inital error, unexpected type <%s>\" % GdObjects.typeof_as_string(current))\n\n\nfunc _notification(event :int) -> void:\n\tif event == NOTIFICATION_PREDELETE:\n\t\tif _base != null:\n\t\t\t_base.notification(event)\n\t\t\t_base = null\n\n\nfunc report_success() -> GdUnitDictionaryAssert:\n\t_base.report_success()\n\treturn self\n\n\nfunc report_error(error :String) -> GdUnitDictionaryAssert:\n\t_base.report_error(error)\n\treturn self\n\n\nfunc failure_message() -> String:\n\treturn _base.failure_message()\n\n\nfunc override_failure_message(message :String) -> GdUnitDictionaryAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.override_failure_message(message)\n\treturn self\n\n\nfunc append_failure_message(message :String) -> GdUnitDictionaryAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.append_failure_message(message)\n\treturn self\n\n\nfunc current_value() -> Variant:\n\treturn _base.current_value()\n\n\nfunc is_null() -> GdUnitDictionaryAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_null()\n\treturn self\n\n\nfunc is_not_null() -> GdUnitDictionaryAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_not_null()\n\treturn self\n\n\nfunc is_equal(expected :Variant) -> GdUnitDictionaryAssert:\n\tvar current :Variant = current_value()\n\tif current == null:\n\t\treturn report_error(GdAssertMessages.error_equal(null, GdAssertMessages.format_dict(expected)))\n\tif not GdObjects.equals(current, expected):\n\t\tvar c := GdAssertMessages.format_dict(current)\n\t\tvar e := GdAssertMessages.format_dict(expected)\n\t\tvar diff := GdDiffTool.string_diff(c, e)\n\t\tvar curent_diff := GdAssertMessages.colored_array_div(diff[1])\n\t\treturn report_error(GdAssertMessages.error_equal(curent_diff, e))\n\treturn report_success()\n\n\nfunc is_not_equal(expected :Variant) -> GdUnitDictionaryAssert:\n\tvar current :Variant = current_value()\n\tif GdObjects.equals(current, expected):\n\t\treturn report_error(GdAssertMessages.error_not_equal(current, expected))\n\treturn report_success()\n\n\n@warning_ignore(\"unused_parameter\", \"shadowed_global_identifier\")\nfunc is_same(expected :Variant) -> GdUnitDictionaryAssert:\n\tvar current :Variant = current_value()\n\tif current == null:\n\t\treturn report_error(GdAssertMessages.error_equal(null, GdAssertMessages.format_dict(expected)))\n\tif not is_same(current, expected):\n\t\tvar c := GdAssertMessages.format_dict(current)\n\t\tvar e := GdAssertMessages.format_dict(expected)\n\t\tvar diff := GdDiffTool.string_diff(c, e)\n\t\tvar curent_diff := GdAssertMessages.colored_array_div(diff[1])\n\t\treturn report_error(GdAssertMessages.error_is_same(curent_diff, e))\n\treturn report_success()\n\n\n@warning_ignore(\"unused_parameter\", \"shadowed_global_identifier\")\nfunc is_not_same(expected :Variant) -> GdUnitDictionaryAssert:\n\tvar current :Variant = current_value()\n\tif is_same(current, expected):\n\t\treturn report_error(GdAssertMessages.error_not_same(current, expected))\n\treturn report_success()\n\n\nfunc is_empty() -> GdUnitDictionaryAssert:\n\tvar current :Variant = current_value()\n\t@warning_ignore(\"unsafe_cast\")\n\tif current == null or not (current as Dictionary).is_empty():\n\t\treturn report_error(GdAssertMessages.error_is_empty(current))\n\treturn report_success()\n\n\nfunc is_not_empty() -> GdUnitDictionaryAssert:\n\tvar current :Variant = current_value()\n\t@warning_ignore(\"unsafe_cast\")\n\tif current == null or (current as Dictionary).is_empty():\n\t\treturn report_error(GdAssertMessages.error_is_not_empty())\n\treturn report_success()\n\n\nfunc has_size(expected: int) -> GdUnitDictionaryAssert:\n\tvar current :Variant = current_value()\n\tif current == null:\n\t\treturn report_error(GdAssertMessages.error_is_not_null())\n\t@warning_ignore(\"unsafe_cast\")\n\tif (current as Dictionary).size() != expected:\n\t\treturn report_error(GdAssertMessages.error_has_size(current, expected))\n\treturn report_success()\n\n\nfunc _contains_keys(expected :Array, compare_mode :GdObjects.COMPARE_MODE) -> GdUnitDictionaryAssert:\n\tvar current :Variant = current_value()\n\tif current == null:\n\t\treturn report_error(GdAssertMessages.error_is_not_null())\n\t# find expected keys\n\t@warning_ignore(\"unsafe_cast\")\n\tvar keys_not_found :Array = expected.filter(_filter_by_key.bind((current as Dictionary).keys(), compare_mode))\n\tif not keys_not_found.is_empty():\n\t\t@warning_ignore(\"unsafe_cast\")\n\t\treturn report_error(GdAssertMessages.error_contains_keys((current as Dictionary).keys() as Array, expected, keys_not_found, compare_mode))\n\treturn report_success()\n\n\nfunc _contains_key_value(key :Variant, value :Variant, compare_mode :GdObjects.COMPARE_MODE) -> GdUnitDictionaryAssert:\n\tvar current :Variant = current_value()\n\tvar expected := [key]\n\tif current == null:\n\t\treturn report_error(GdAssertMessages.error_is_not_null())\n\tvar dict_current: Dictionary = current\n\tvar keys_not_found :Array = expected.filter(_filter_by_key.bind(dict_current.keys(), compare_mode))\n\tif not keys_not_found.is_empty():\n\t\treturn report_error(GdAssertMessages.error_contains_keys(dict_current.keys() as Array, expected, keys_not_found, compare_mode))\n\tif not GdObjects.equals(dict_current[key], value, false, compare_mode):\n\t\treturn report_error(GdAssertMessages.error_contains_key_value(key, value, dict_current[key], compare_mode))\n\treturn report_success()\n\n\nfunc _not_contains_keys(expected :Array, compare_mode :GdObjects.COMPARE_MODE) -> GdUnitDictionaryAssert:\n\tvar current :Variant = current_value()\n\tif current == null:\n\t\treturn report_error(GdAssertMessages.error_is_not_null())\n\tvar dict_current: Dictionary = current\n\tvar keys_found :Array = dict_current.keys().filter(_filter_by_key.bind(expected, compare_mode, true))\n\tif not keys_found.is_empty():\n\t\treturn report_error(GdAssertMessages.error_not_contains_keys(dict_current.keys() as Array, expected, keys_found, compare_mode))\n\treturn report_success()\n\n\nfunc contains_keys(expected :Array) -> GdUnitDictionaryAssert:\n\treturn _contains_keys(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST)\n\n\nfunc contains_key_value(key :Variant, value :Variant) -> GdUnitDictionaryAssert:\n\treturn _contains_key_value(key, value, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST)\n\n\nfunc not_contains_keys(expected :Array) -> GdUnitDictionaryAssert:\n\treturn _not_contains_keys(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST)\n\n\nfunc contains_same_keys(expected :Array) -> GdUnitDictionaryAssert:\n\treturn _contains_keys(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE)\n\n\nfunc contains_same_key_value(key :Variant, value :Variant) -> GdUnitDictionaryAssert:\n\treturn _contains_key_value(key, value, GdObjects.COMPARE_MODE.OBJECT_REFERENCE)\n\n\nfunc not_contains_same_keys(expected :Array) -> GdUnitDictionaryAssert:\n\treturn _not_contains_keys(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE)\n\n\nfunc _filter_by_key(element :Variant, values :Array, compare_mode :GdObjects.COMPARE_MODE, is_not :bool = false) -> bool:\n\tfor key :Variant in values:\n\t\tif GdObjects.equals(key, element, false, compare_mode):\n\t\t\treturn is_not\n\treturn !is_not\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd.uid",
    "content": "uid://o1uqndxexyaf\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd",
    "content": "extends GdUnitFailureAssert\n\nconst GdUnitTools := preload(\"res://addons/gdUnit4/src/core/GdUnitTools.gd\")\n\nvar _is_failed := false\nvar _failure_message :String\n\n\nfunc _set_do_expect_fail(enabled :bool = true) -> void:\n\tEngine.set_meta(GdUnitConstants.EXPECT_ASSERT_REPORT_FAILURES, enabled)\n\n\nfunc execute_and_await(assertion :Callable, do_await := true) -> GdUnitFailureAssert:\n\t# do not report any failure from the original assertion we want to test\n\t_set_do_expect_fail(true)\n\tvar thread_context := GdUnitThreadManager.get_current_context()\n\tthread_context.set_assert(null)\n\t@warning_ignore(\"return_value_discarded\")\n\tGdUnitSignals.instance().gdunit_set_test_failed.connect(_on_test_failed)\n\t# execute the given assertion as callable\n\tif do_await:\n\t\tawait assertion.call()\n\telse:\n\t\tassertion.call()\n\t_set_do_expect_fail(false)\n\t# get the assert instance from current tread context\n\tvar current_assert := thread_context.get_assert()\n\tif not is_instance_of(current_assert, GdUnitAssert):\n\t\t_is_failed = true\n\t\t_failure_message = \"Invalid Callable! It must be a callable of 'GdUnitAssert'\"\n\t\treturn self\n\t@warning_ignore(\"unsafe_method_access\")\n\t_failure_message = current_assert.failure_message()\n\treturn self\n\n\nfunc execute(assertion :Callable) -> GdUnitFailureAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\texecute_and_await(assertion, false)\n\treturn self\n\n\nfunc _on_test_failed(value :bool) -> void:\n\t_is_failed = value\n\n\n@warning_ignore(\"unused_parameter\")\nfunc is_equal(_expected: Variant) -> GdUnitFailureAssert:\n\treturn _report_error(\"Not implemented\")\n\n\n@warning_ignore(\"unused_parameter\")\nfunc is_not_equal(_expected: Variant) -> GdUnitFailureAssert:\n\treturn _report_error(\"Not implemented\")\n\n\nfunc is_null() -> GdUnitFailureAssert:\n\treturn _report_error(\"Not implemented\")\n\n\nfunc is_not_null() -> GdUnitFailureAssert:\n\treturn _report_error(\"Not implemented\")\n\n\nfunc is_success() -> GdUnitFailureAssert:\n\tif _is_failed:\n\t\treturn _report_error(\"Expect: assertion ends successfully.\")\n\treturn self\n\n\nfunc is_failed() -> GdUnitFailureAssert:\n\tif not _is_failed:\n\t\treturn _report_error(\"Expect: assertion fails.\")\n\treturn self\n\n\nfunc has_line(expected :int) -> GdUnitFailureAssert:\n\tvar current := GdAssertReports.get_last_error_line_number()\n\tif current != expected:\n\t\treturn _report_error(\"Expect: to failed on line '%d'\\n but was '%d'.\" % [expected, current])\n\treturn self\n\n\nfunc has_message(expected :String) -> GdUnitFailureAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\tis_failed()\n\tvar expected_error := GdUnitTools.normalize_text(GdUnitTools.richtext_normalize(expected))\n\tvar current_error := GdUnitTools.normalize_text(GdUnitTools.richtext_normalize(_failure_message))\n\tif current_error != expected_error:\n\t\tvar diffs := GdDiffTool.string_diff(current_error, expected_error)\n\t\tvar current := GdAssertMessages.colored_array_div(diffs[1])\n\t\treturn _report_error(GdAssertMessages.error_not_same_error(current, expected_error))\n\treturn self\n\n\nfunc contains_message(expected :String) -> GdUnitFailureAssert:\n\tvar expected_error := GdUnitTools.normalize_text(expected)\n\tvar current_error := GdUnitTools.normalize_text(GdUnitTools.richtext_normalize(_failure_message))\n\tif not current_error.contains(expected_error):\n\t\tvar diffs := GdDiffTool.string_diff(current_error, expected_error)\n\t\tvar current := GdAssertMessages.colored_array_div(diffs[1])\n\t\treturn _report_error(GdAssertMessages.error_not_same_error(current, expected_error))\n\treturn self\n\n\nfunc starts_with_message(expected :String) -> GdUnitFailureAssert:\n\tvar expected_error := GdUnitTools.normalize_text(expected)\n\tvar current_error := GdUnitTools.normalize_text(GdUnitTools.richtext_normalize(_failure_message))\n\tif current_error.find(expected_error) != 0:\n\t\tvar diffs := GdDiffTool.string_diff(current_error, expected_error)\n\t\tvar current := GdAssertMessages.colored_array_div(diffs[1])\n\t\treturn _report_error(GdAssertMessages.error_not_same_error(current, expected_error))\n\treturn self\n\n\nfunc _report_error(error_message :String, failure_line_number: int = -1) -> GdUnitAssert:\n\tvar line_number := failure_line_number if failure_line_number != -1 else GdUnitAssertions.get_line_number()\n\tGdAssertReports.report_error(error_message, line_number)\n\treturn self\n\n\nfunc _report_success() -> GdUnitFailureAssert:\n\tGdAssertReports.report_success()\n\treturn self\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd.uid",
    "content": "uid://dirorofm3wv1d\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd",
    "content": "extends GdUnitFileAssert\n\nconst GdUnitTools := preload(\"res://addons/gdUnit4/src/core/GdUnitTools.gd\")\n\nvar _base: GdUnitAssertImpl\n\n\nfunc _init(current :Variant) -> void:\n\t_base = GdUnitAssertImpl.new(current)\n\t# save the actual assert instance on the current thread context\n\tGdUnitThreadManager.get_current_context().set_assert(self)\n\tif not GdUnitAssertions.validate_value_type(current, TYPE_STRING):\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\treport_error(\"GdUnitFileAssert inital error, unexpected type <%s>\" % GdObjects.typeof_as_string(current))\n\n\nfunc _notification(event :int) -> void:\n\tif event == NOTIFICATION_PREDELETE:\n\t\tif _base != null:\n\t\t\t_base.notification(event)\n\t\t\t_base = null\n\n\nfunc current_value() -> String:\n\treturn _base.current_value()\n\n\nfunc report_success() -> GdUnitFileAssert:\n\t_base.report_success()\n\treturn self\n\n\nfunc report_error(error :String) -> GdUnitFileAssert:\n\t_base.report_error(error)\n\treturn self\n\n\nfunc failure_message() -> String:\n\treturn _base.failure_message()\n\n\nfunc override_failure_message(message :String) -> GdUnitFileAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.override_failure_message(message)\n\treturn self\n\n\nfunc append_failure_message(message :String) -> GdUnitFileAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.append_failure_message(message)\n\treturn self\n\n\nfunc is_equal(expected :Variant) -> GdUnitFileAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_equal(expected)\n\treturn self\n\n\nfunc is_not_equal(expected :Variant) -> GdUnitFileAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_not_equal(expected)\n\treturn self\n\n\nfunc is_file() -> GdUnitFileAssert:\n\tvar current := current_value()\n\tif FileAccess.open(current, FileAccess.READ) == null:\n\t\treturn report_error(\"Is not a file '%s', error code %s\" % [current, FileAccess.get_open_error()])\n\treturn report_success()\n\n\nfunc exists() -> GdUnitFileAssert:\n\tvar current := current_value()\n\tif not FileAccess.file_exists(current):\n\t\treturn report_error(\"The file '%s' not exists\" %current)\n\treturn report_success()\n\n\nfunc is_script() -> GdUnitFileAssert:\n\tvar current := current_value()\n\tif FileAccess.open(current, FileAccess.READ) == null:\n\t\treturn report_error(\"Can't acces the file '%s'! Error code %s\" % [current, FileAccess.get_open_error()])\n\n\tvar script := load(current)\n\tif not script is GDScript:\n\t\treturn report_error(\"The file '%s' is not a GdScript\" % current)\n\treturn report_success()\n\n\nfunc contains_exactly(expected_rows: Array) -> GdUnitFileAssert:\n\tvar current := current_value()\n\tif FileAccess.open(current, FileAccess.READ) == null:\n\t\treturn report_error(\"Can't acces the file '%s'! Error code %s\" % [current, FileAccess.get_open_error()])\n\n\tvar script: GDScript = load(current)\n\tif script is GDScript:\n\t\tvar source_code := GdScriptParser.to_unix_format(script.source_code)\n\t\tvar rows := Array(source_code.split(\"\\n\"))\n\t\tGdUnitArrayAssertImpl.new(rows).contains_exactly(expected_rows)\n\treturn self\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd.uid",
    "content": "uid://c1lt173ig6uup\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd",
    "content": "extends GdUnitFloatAssert\n\nvar _base: GdUnitAssertImpl\n\n\nfunc _init(current :Variant) -> void:\n\t_base = GdUnitAssertImpl.new(current)\n\t# save the actual assert instance on the current thread context\n\tGdUnitThreadManager.get_current_context().set_assert(self)\n\tif not GdUnitAssertions.validate_value_type(current, TYPE_FLOAT):\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\treport_error(\"GdUnitFloatAssert inital error, unexpected type <%s>\" % GdObjects.typeof_as_string(current))\n\n\nfunc _notification(event :int) -> void:\n\tif event == NOTIFICATION_PREDELETE:\n\t\tif _base != null:\n\t\t\t_base.notification(event)\n\t\t\t_base = null\n\n\nfunc current_value() -> Variant:\n\treturn _base.current_value()\n\n\nfunc report_success() -> GdUnitFloatAssert:\n\t_base.report_success()\n\treturn self\n\n\nfunc report_error(error :String) -> GdUnitFloatAssert:\n\t_base.report_error(error)\n\treturn self\n\n\nfunc failure_message() -> String:\n\treturn _base.failure_message()\n\n\nfunc override_failure_message(message :String) -> GdUnitFloatAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.override_failure_message(message)\n\treturn self\n\n\nfunc append_failure_message(message :String) -> GdUnitFloatAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.append_failure_message(message)\n\treturn self\n\n\nfunc is_null() -> GdUnitFloatAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_null()\n\treturn self\n\n\nfunc is_not_null() -> GdUnitFloatAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_not_null()\n\treturn self\n\n\nfunc is_equal(expected :Variant) -> GdUnitFloatAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_equal(expected)\n\treturn self\n\n\nfunc is_not_equal(expected :Variant) -> GdUnitFloatAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_not_equal(expected)\n\treturn self\n\n\n@warning_ignore(\"shadowed_global_identifier\")\nfunc is_equal_approx(expected :float, approx :float) -> GdUnitFloatAssert:\n\treturn is_between(expected-approx, expected+approx)\n\n\nfunc is_less(expected :float) -> GdUnitFloatAssert:\n\tvar current :Variant = current_value()\n\tif current == null or current >= expected:\n\t\treturn report_error(GdAssertMessages.error_is_value(Comparator.LESS_THAN, current, expected))\n\treturn report_success()\n\n\nfunc is_less_equal(expected :float) -> GdUnitFloatAssert:\n\tvar current :Variant = current_value()\n\tif current == null or current > expected:\n\t\treturn report_error(GdAssertMessages.error_is_value(Comparator.LESS_EQUAL, current, expected))\n\treturn report_success()\n\n\nfunc is_greater(expected :float) -> GdUnitFloatAssert:\n\tvar current :Variant = current_value()\n\tif current == null or current <= expected:\n\t\treturn report_error(GdAssertMessages.error_is_value(Comparator.GREATER_THAN, current, expected))\n\treturn report_success()\n\n\nfunc is_greater_equal(expected :float) -> GdUnitFloatAssert:\n\tvar current :Variant = current_value()\n\tif current == null or current < expected:\n\t\treturn report_error(GdAssertMessages.error_is_value(Comparator.GREATER_EQUAL, current, expected))\n\treturn report_success()\n\n\nfunc is_negative() -> GdUnitFloatAssert:\n\tvar current :Variant = current_value()\n\tif current == null or current >= 0.0:\n\t\treturn report_error(GdAssertMessages.error_is_negative(current))\n\treturn report_success()\n\n\nfunc is_not_negative() -> GdUnitFloatAssert:\n\tvar current :Variant = current_value()\n\tif current == null or current < 0.0:\n\t\treturn report_error(GdAssertMessages.error_is_not_negative(current))\n\treturn report_success()\n\n\nfunc is_zero() -> GdUnitFloatAssert:\n\tvar current :Variant = current_value()\n\t@warning_ignore(\"unsafe_cast\")\n\tif current == null or not is_equal_approx(0.00000000, current as float):\n\t\treturn report_error(GdAssertMessages.error_is_zero(current))\n\treturn report_success()\n\n\nfunc is_not_zero() -> GdUnitFloatAssert:\n\tvar current :Variant = current_value()\n\t@warning_ignore(\"unsafe_cast\")\n\tif current == null or is_equal_approx(0.00000000, current as float):\n\t\treturn report_error(GdAssertMessages.error_is_not_zero())\n\treturn report_success()\n\n\nfunc is_in(expected :Array) -> GdUnitFloatAssert:\n\tvar current :Variant = current_value()\n\tif not expected.has(current):\n\t\treturn report_error(GdAssertMessages.error_is_in(current, expected))\n\treturn report_success()\n\n\nfunc is_not_in(expected :Array) -> GdUnitFloatAssert:\n\tvar current :Variant = current_value()\n\tif expected.has(current):\n\t\treturn report_error(GdAssertMessages.error_is_not_in(current, expected))\n\treturn report_success()\n\n\nfunc is_between(from :float, to :float) -> GdUnitFloatAssert:\n\tvar current :Variant = current_value()\n\tif current == null or current < from or current > to:\n\t\treturn report_error(GdAssertMessages.error_is_value(Comparator.BETWEEN_EQUAL, current, from, to))\n\treturn report_success()\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd.uid",
    "content": "uid://pcig0tou0srk\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd",
    "content": "extends GdUnitFuncAssert\n\n\nconst GdUnitTools := preload(\"res://addons/gdUnit4/src/core/GdUnitTools.gd\")\nconst DEFAULT_TIMEOUT := 2000\n\n\nvar _current_value_provider :ValueProvider\nvar _current_failure_message :String = \"\"\nvar _custom_failure_message :String = \"\"\nvar _additional_failure_message: String = \"\"\nvar _line_number := -1\nvar _timeout := DEFAULT_TIMEOUT\nvar _interrupted := false\nvar _sleep_timer :Timer = null\n\n\nfunc _init(instance :Object, func_name :String, args := Array()) -> void:\n\t_line_number = GdUnitAssertions.get_line_number()\n\tGdAssertReports.reset_last_error_line_number()\n\t# save the actual assert instance on the current thread context\n\tGdUnitThreadManager.get_current_context().set_assert(self)\n\t# verify at first the function name exists\n\tif not instance.has_method(func_name):\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\treport_error(\"The function '%s' do not exists checked instance '%s'.\" % [func_name, instance])\n\t\t_interrupted = true\n\telse:\n\t\t_current_value_provider = CallBackValueProvider.new(instance, func_name, args)\n\n\nfunc _notification(what :int) -> void:\n\tif what == NOTIFICATION_PREDELETE:\n\t\t_interrupted = true\n\t\tvar main_node :Node = (Engine.get_main_loop() as SceneTree).root\n\t\tif is_instance_valid(_current_value_provider):\n\t\t\t_current_value_provider.dispose()\n\t\t\t_current_value_provider = null\n\t\tif is_instance_valid(_sleep_timer):\n\t\t\t_sleep_timer.set_wait_time(0.0001)\n\t\t\t_sleep_timer.stop()\n\t\t\tmain_node.remove_child(_sleep_timer)\n\t\t\t_sleep_timer.free()\n\t\t\t_sleep_timer = null\n\n\nfunc report_success() -> GdUnitFuncAssert:\n\tGdAssertReports.report_success()\n\treturn self\n\n\nfunc report_error(failure :String) -> GdUnitFuncAssert:\n\t_current_failure_message = GdAssertMessages.build_failure_message(failure, _additional_failure_message, _custom_failure_message)\n\tGdAssertReports.report_error(_current_failure_message, _line_number)\n\treturn self\n\n\nfunc failure_message() -> String:\n\treturn _current_failure_message\n\n\nfunc override_failure_message(message :String) -> GdUnitFuncAssert:\n\t_custom_failure_message = message\n\treturn self\n\n\nfunc append_failure_message(message :String) -> GdUnitFuncAssert:\n\t_additional_failure_message = message\n\treturn self\n\n\nfunc wait_until(timeout := 2000) -> GdUnitFuncAssert:\n\tif timeout <= 0:\n\t\tpush_warning(\"Invalid timeout param, alloed timeouts must be grater than 0. Use default timeout instead\")\n\t\t_timeout = DEFAULT_TIMEOUT\n\telse:\n\t\t_timeout = timeout\n\treturn self\n\n\nfunc is_null() -> GdUnitFuncAssert:\n\tawait _validate_callback(cb_is_null)\n\treturn self\n\n\nfunc is_not_null() -> GdUnitFuncAssert:\n\tawait _validate_callback(cb_is_not_null)\n\treturn self\n\n\nfunc is_false() -> GdUnitFuncAssert:\n\tawait _validate_callback(cb_is_false)\n\treturn self\n\n\nfunc is_true() -> GdUnitFuncAssert:\n\tawait _validate_callback(cb_is_true)\n\treturn self\n\n\nfunc is_equal(expected :Variant) -> GdUnitFuncAssert:\n\tawait _validate_callback(cb_is_equal, expected)\n\treturn self\n\n\nfunc is_not_equal(expected :Variant) -> GdUnitFuncAssert:\n\tawait _validate_callback(cb_is_not_equal, expected)\n\treturn self\n\n\n# we need actually to define this Callable as functions otherwise we results into leaked scripts here\n# this is actually a Godot bug and needs this kind of workaround\nfunc cb_is_null(c :Variant, _e :Variant) -> bool: return c == null\nfunc cb_is_not_null(c :Variant, _e :Variant) -> bool: return c != null\nfunc cb_is_false(c :Variant, _e :Variant) -> bool: return c == false\nfunc cb_is_true(c :Variant, _e :Variant) -> bool: return c == true\nfunc cb_is_equal(c :Variant, e :Variant) -> bool: return GdObjects.equals(c,e)\nfunc cb_is_not_equal(c :Variant, e :Variant) -> bool: return not GdObjects.equals(c, e)\n\n\nfunc do_interrupt() -> void:\n\t_interrupted = true\n\n\nfunc _validate_callback(predicate :Callable, expected :Variant = null) -> void:\n\tif _interrupted:\n\t\treturn\n\tGdUnitMemoryObserver.guard_instance(self)\n\tvar time_scale := Engine.get_time_scale()\n\tvar timer := Timer.new()\n\ttimer.set_name(\"gdunit_funcassert_interrupt_timer_%d\" % timer.get_instance_id())\n\tvar scene_tree := Engine.get_main_loop() as SceneTree\n\tscene_tree.root.add_child(timer)\n\ttimer.add_to_group(\"GdUnitTimers\")\n\t@warning_ignore(\"return_value_discarded\")\n\ttimer.timeout.connect(do_interrupt, CONNECT_DEFERRED)\n\ttimer.set_one_shot(true)\n\ttimer.start((_timeout/1000.0)*time_scale)\n\t_sleep_timer = Timer.new()\n\t_sleep_timer.set_name(\"gdunit_funcassert_sleep_timer_%d\" % _sleep_timer.get_instance_id() )\n\tscene_tree.root.add_child(_sleep_timer)\n\n\twhile true:\n\t\tvar current :Variant = await next_current_value()\n\t\t# is interupted or predicate success\n\t\tif _interrupted or predicate.call(current, expected):\n\t\t\tbreak\n\t\tif is_instance_valid(_sleep_timer):\n\t\t\t_sleep_timer.start(0.05)\n\t\t\tawait _sleep_timer.timeout\n\n\t_sleep_timer.stop()\n\tawait scene_tree.process_frame\n\tif _interrupted:\n\t\t# https://github.com/godotengine/godot/issues/73052\n\t\t#var predicate_name = predicate.get_method()\n\t\tvar predicate_name :String = str(predicate).split('::')[1]\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\treport_error(GdAssertMessages.error_interrupted(\n\t\t\tpredicate_name.strip_edges().trim_prefix(\"cb_\"),\n\t\t\texpected,\n\t\t\tLocalTime.elapsed(_timeout)\n\t\t\t)\n\t\t)\n\telse:\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\treport_success()\n\t_sleep_timer.free()\n\ttimer.free()\n\tGdUnitMemoryObserver.unguard_instance(self)\n\n\nfunc next_current_value() -> Variant:\n\t@warning_ignore(\"redundant_await\")\n\tif is_instance_valid(_current_value_provider):\n\t\treturn await _current_value_provider.get_value()\n\treturn \"invalid value\"\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd.uid",
    "content": "uid://bgttxwcyis3mu\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd",
    "content": "extends GdUnitGodotErrorAssert\n\nvar _current_error_message :String\nvar _callable :Callable\n\n\nfunc _init(callable :Callable) -> void:\n\t# we only support Godot 4.1.x+ because of await issue https://github.com/godotengine/godot/issues/80292\n\tassert(Engine.get_version_info().hex >= 0x40100,\n\t\t\t\"This assertion is not supported for Godot 4.0.x. Please upgrade to the minimum version Godot 4.1.0!\")\n\t# save the actual assert instance on the current thread context\n\tGdUnitThreadManager.get_current_context().set_assert(self)\n\tGdAssertReports.reset_last_error_line_number()\n\t_callable = callable\n\n\nfunc _execute() -> Array[ErrorLogEntry]:\n\t# execute the given code and monitor for runtime errors\n\tif _callable == null or not _callable.is_valid():\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\t_report_error(\"Invalid Callable '%s'\" % _callable)\n\telse:\n\t\tawait _callable.call()\n\treturn await _error_monitor().scan(true)\n\n\nfunc _error_monitor() -> GodotGdErrorMonitor:\n\treturn GdUnitThreadManager.get_current_context().get_execution_context().error_monitor\n\n\nfunc failure_message() -> String:\n\treturn _current_error_message\n\n\nfunc _report_success() -> GdUnitAssert:\n\tGdAssertReports.report_success()\n\treturn self\n\n\nfunc _report_error(error_message :String, failure_line_number: int = -1) -> GdUnitAssert:\n\tvar line_number := failure_line_number if failure_line_number != -1 else GdUnitAssertions.get_line_number()\n\t_current_error_message = error_message\n\tGdAssertReports.report_error(error_message, line_number)\n\treturn self\n\n\nfunc _has_log_entry(log_entries :Array[ErrorLogEntry], type :ErrorLogEntry.TYPE, error :String) -> bool:\n\tfor entry in log_entries:\n\t\tif entry._type == type and entry._message == error:\n\t\t\t# Erase the log entry we already handled it by this assertion, otherwise it will report at twice\n\t\t\t_error_monitor().erase_log_entry(entry)\n\t\t\treturn true\n\treturn false\n\n\nfunc _to_list(log_entries :Array[ErrorLogEntry]) -> String:\n\tif log_entries.is_empty():\n\t\treturn \"no errors\"\n\tif log_entries.size() == 1:\n\t\treturn log_entries[0]._message\n\tvar value := \"\"\n\tfor entry in log_entries:\n\t\tvalue += \"'%s'\\n\" % entry._message\n\treturn value\n\n\nfunc is_success() -> GdUnitGodotErrorAssert:\n\tvar log_entries := await _execute()\n\tif log_entries.is_empty():\n\t\treturn _report_success()\n\treturn _report_error(\"\"\"\n\t\tExpecting: no error's are ocured.\n\t\t\tbut found: '%s'\n\t\t\"\"\".dedent().trim_prefix(\"\\n\") % _to_list(log_entries))\n\n\nfunc is_runtime_error(expected_error :String) -> GdUnitGodotErrorAssert:\n\tvar log_entries := await _execute()\n\tif _has_log_entry(log_entries, ErrorLogEntry.TYPE.SCRIPT_ERROR, expected_error):\n\t\treturn _report_success()\n\treturn _report_error(\"\"\"\n\t\tExpecting: a runtime error is triggered.\n\t\t\tmessage: '%s'\n\t\t\tfound: %s\n\t\t\"\"\".dedent().trim_prefix(\"\\n\") % [expected_error, _to_list(log_entries)])\n\n\nfunc is_push_warning(expected_warning :String) -> GdUnitGodotErrorAssert:\n\tvar log_entries := await _execute()\n\tif _has_log_entry(log_entries, ErrorLogEntry.TYPE.PUSH_WARNING, expected_warning):\n\t\treturn _report_success()\n\treturn _report_error(\"\"\"\n\t\tExpecting: push_warning() is called.\n\t\t\tmessage: '%s'\n\t\t\tfound: %s\n\t\t\"\"\".dedent().trim_prefix(\"\\n\") % [expected_warning, _to_list(log_entries)])\n\n\nfunc is_push_error(expected_error :String) -> GdUnitGodotErrorAssert:\n\tvar log_entries := await _execute()\n\tif _has_log_entry(log_entries, ErrorLogEntry.TYPE.PUSH_ERROR, expected_error):\n\t\treturn _report_success()\n\treturn _report_error(\"\"\"\n\t\tExpecting: push_error() is called.\n\t\t\tmessage: '%s'\n\t\t\tfound: %s\n\t\t\"\"\".dedent().trim_prefix(\"\\n\") % [expected_error, _to_list(log_entries)])\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd.uid",
    "content": "uid://bgrbjg0rqe188\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd",
    "content": "extends GdUnitIntAssert\n\nvar _base: GdUnitAssertImpl\n\n\nfunc _init(current :Variant) -> void:\n\t_base = GdUnitAssertImpl.new(current)\n\t# save the actual assert instance on the current thread context\n\tGdUnitThreadManager.get_current_context().set_assert(self)\n\tif not GdUnitAssertions.validate_value_type(current, TYPE_INT):\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\treport_error(\"GdUnitIntAssert inital error, unexpected type <%s>\" % GdObjects.typeof_as_string(current))\n\n\nfunc _notification(event :int) -> void:\n\tif event == NOTIFICATION_PREDELETE:\n\t\tif _base != null:\n\t\t\t_base.notification(event)\n\t\t\t_base = null\n\n\nfunc current_value() -> Variant:\n\treturn _base.current_value()\n\n\nfunc report_success() -> GdUnitIntAssert:\n\t_base.report_success()\n\treturn self\n\n\nfunc report_error(error :String) -> GdUnitIntAssert:\n\t_base.report_error(error)\n\treturn self\n\n\nfunc failure_message() -> String:\n\treturn _base.failure_message()\n\n\nfunc override_failure_message(message :String) -> GdUnitIntAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.override_failure_message(message)\n\treturn self\n\n\nfunc append_failure_message(message :String) -> GdUnitIntAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.append_failure_message(message)\n\treturn self\n\n\nfunc is_null() -> GdUnitIntAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_null()\n\treturn self\n\n\nfunc is_not_null() -> GdUnitIntAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_not_null()\n\treturn self\n\n\nfunc is_equal(expected :Variant) -> GdUnitIntAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_equal(expected)\n\treturn self\n\n\nfunc is_not_equal(expected :Variant) -> GdUnitIntAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_not_equal(expected)\n\treturn self\n\n\nfunc is_less(expected :int) -> GdUnitIntAssert:\n\tvar current :Variant = current_value()\n\tif current == null or current >= expected:\n\t\treturn report_error(GdAssertMessages.error_is_value(Comparator.LESS_THAN, current, expected))\n\treturn report_success()\n\n\nfunc is_less_equal(expected :int) -> GdUnitIntAssert:\n\tvar current :Variant = current_value()\n\tif current == null or current > expected:\n\t\treturn report_error(GdAssertMessages.error_is_value(Comparator.LESS_EQUAL, current, expected))\n\treturn report_success()\n\n\nfunc is_greater(expected :int) -> GdUnitIntAssert:\n\tvar current :Variant = current_value()\n\tif current == null or current <= expected:\n\t\treturn report_error(GdAssertMessages.error_is_value(Comparator.GREATER_THAN, current, expected))\n\treturn report_success()\n\n\nfunc is_greater_equal(expected :int) -> GdUnitIntAssert:\n\tvar current :Variant = current_value()\n\tif current == null or current < expected:\n\t\treturn report_error(GdAssertMessages.error_is_value(Comparator.GREATER_EQUAL, current, expected))\n\treturn report_success()\n\n\nfunc is_even() -> GdUnitIntAssert:\n\tvar current :Variant = current_value()\n\tif current == null or current % 2 != 0:\n\t\treturn report_error(GdAssertMessages.error_is_even(current))\n\treturn report_success()\n\n\nfunc is_odd() -> GdUnitIntAssert:\n\tvar current :Variant = current_value()\n\tif current == null or current % 2 == 0:\n\t\treturn report_error(GdAssertMessages.error_is_odd(current))\n\treturn report_success()\n\n\nfunc is_negative() -> GdUnitIntAssert:\n\tvar current :Variant = current_value()\n\tif current == null or current >= 0:\n\t\treturn report_error(GdAssertMessages.error_is_negative(current))\n\treturn report_success()\n\n\nfunc is_not_negative() -> GdUnitIntAssert:\n\tvar current :Variant = current_value()\n\tif current == null or current < 0:\n\t\treturn report_error(GdAssertMessages.error_is_not_negative(current))\n\treturn report_success()\n\n\nfunc is_zero() -> GdUnitIntAssert:\n\tvar current :Variant = current_value()\n\tif current != 0:\n\t\treturn report_error(GdAssertMessages.error_is_zero(current))\n\treturn report_success()\n\n\nfunc is_not_zero() -> GdUnitIntAssert:\n\tvar current :Variant= current_value()\n\tif current == 0:\n\t\treturn report_error(GdAssertMessages.error_is_not_zero())\n\treturn report_success()\n\n\nfunc is_in(expected :Array) -> GdUnitIntAssert:\n\tvar current :Variant = current_value()\n\tif not expected.has(current):\n\t\treturn report_error(GdAssertMessages.error_is_in(current, expected))\n\treturn report_success()\n\n\nfunc is_not_in(expected :Array) -> GdUnitIntAssert:\n\tvar current :Variant = current_value()\n\tif expected.has(current):\n\t\treturn report_error(GdAssertMessages.error_is_not_in(current, expected))\n\treturn report_success()\n\n\nfunc is_between(from :int, to :int) -> GdUnitIntAssert:\n\tvar current :Variant = current_value()\n\tif current == null or current < from or current > to:\n\t\treturn report_error(GdAssertMessages.error_is_value(Comparator.BETWEEN_EQUAL, current, from, to))\n\treturn report_success()\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd.uid",
    "content": "uid://bhcsxb02dv1vj\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd",
    "content": "extends GdUnitObjectAssert\n\nvar _base: GdUnitAssertImpl\n\n\nfunc _init(current :Variant) -> void:\n\t_base = GdUnitAssertImpl.new(current)\n\t# save the actual assert instance on the current thread context\n\tGdUnitThreadManager.get_current_context().set_assert(self)\n\tif (current != null\n\t\tand (GdUnitAssertions.validate_value_type(current, TYPE_BOOL)\n\t\tor GdUnitAssertions.validate_value_type(current, TYPE_INT)\n\t\tor GdUnitAssertions.validate_value_type(current, TYPE_FLOAT)\n\t\tor GdUnitAssertions.validate_value_type(current, TYPE_STRING))):\n\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\treport_error(\"GdUnitObjectAssert inital error, unexpected type <%s>\" % GdObjects.typeof_as_string(current))\n\n\nfunc _notification(event :int) -> void:\n\tif event == NOTIFICATION_PREDELETE:\n\t\tif _base != null:\n\t\t\t_base.notification(event)\n\t\t\t_base = null\n\n\nfunc current_value() -> Variant:\n\treturn _base.current_value()\n\n\nfunc report_success() -> GdUnitObjectAssert:\n\t_base.report_success()\n\treturn self\n\n\nfunc report_error(error :String) -> GdUnitObjectAssert:\n\t_base.report_error(error)\n\treturn self\n\n\nfunc failure_message() -> String:\n\treturn _base.failure_message()\n\n\nfunc override_failure_message(message :String) -> GdUnitObjectAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.override_failure_message(message)\n\treturn self\n\n\nfunc append_failure_message(message :String) -> GdUnitObjectAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.append_failure_message(message)\n\treturn self\n\n\nfunc is_equal(expected :Variant) -> GdUnitObjectAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_equal(expected)\n\treturn self\n\n\nfunc is_not_equal(expected :Variant) -> GdUnitObjectAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_not_equal(expected)\n\treturn self\n\n\nfunc is_null() -> GdUnitObjectAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_null()\n\treturn self\n\n\nfunc is_not_null() -> GdUnitObjectAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_not_null()\n\treturn self\n\n\n@warning_ignore(\"shadowed_global_identifier\")\nfunc is_same(expected :Variant) -> GdUnitObjectAssert:\n\tvar current :Variant = current_value()\n\tif not is_same(current, expected):\n\t\treturn report_error(GdAssertMessages.error_is_same(current, expected))\n\treturn report_success()\n\n\nfunc is_not_same(expected :Variant) -> GdUnitObjectAssert:\n\tvar current :Variant = current_value()\n\tif is_same(current, expected):\n\t\treturn report_error(GdAssertMessages.error_not_same(current, expected))\n\treturn report_success()\n\n\nfunc is_instanceof(type :Object) -> GdUnitObjectAssert:\n\tvar current :Variant = current_value()\n\tif current == null or not is_instance_of(current, type):\n\t\tvar result_expected: = GdObjects.extract_class_name(type)\n\t\tvar result_current: = GdObjects.extract_class_name(current)\n\t\treturn report_error(GdAssertMessages.error_is_instanceof(result_current, result_expected))\n\treturn report_success()\n\n\nfunc is_not_instanceof(type :Variant) -> GdUnitObjectAssert:\n\tvar current :Variant = current_value()\n\tif is_instance_of(current, type):\n\t\tvar result: = GdObjects.extract_class_name(type)\n\t\tif result.is_success():\n\t\t\treturn report_error(\"Expected not be a instance of <%s>\" % str(result.value()))\n\n\t\tpush_error(\"Internal ERROR: %s\" % result.error_message())\n\t\treturn self\n\treturn report_success()\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd.uid",
    "content": "uid://c4tlt0w3jkdal\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd",
    "content": "extends GdUnitResultAssert\n\nvar _base: GdUnitAssertImpl\n\n\nfunc _init(current :Variant) -> void:\n\t_base = GdUnitAssertImpl.new(current)\n\t# save the actual assert instance on the current thread context\n\tGdUnitThreadManager.get_current_context().set_assert(self)\n\tif not validate_value_type(current):\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\treport_error(\"GdUnitResultAssert inital error, unexpected type <%s>\" % GdObjects.typeof_as_string(current))\n\n\nfunc _notification(event :int) -> void:\n\tif event == NOTIFICATION_PREDELETE:\n\t\tif _base != null:\n\t\t\t_base.notification(event)\n\t\t\t_base = null\n\n\nfunc validate_value_type(value :Variant) -> bool:\n\treturn value == null or value is GdUnitResult\n\n\nfunc current_value() -> GdUnitResult:\n\treturn _base.current_value()\n\n\nfunc report_success() -> GdUnitResultAssert:\n\t_base.report_success()\n\treturn self\n\n\nfunc report_error(error :String) -> GdUnitResultAssert:\n\t_base.report_error(error)\n\treturn self\n\n\nfunc failure_message() -> String:\n\treturn _base.failure_message()\n\n\nfunc override_failure_message(message :String) -> GdUnitResultAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.override_failure_message(message)\n\treturn self\n\n\nfunc append_failure_message(message :String) -> GdUnitResultAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.append_failure_message(message)\n\treturn self\n\n\nfunc is_null() -> GdUnitResultAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_null()\n\treturn self\n\n\nfunc is_not_null() -> GdUnitResultAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_not_null()\n\treturn self\n\n\nfunc is_empty() -> GdUnitResultAssert:\n\tvar result := current_value()\n\tif result == null or not result.is_empty():\n\t\treturn report_error(GdAssertMessages.error_result_is_empty(result))\n\treturn report_success()\n\n\nfunc is_success() -> GdUnitResultAssert:\n\tvar result := current_value()\n\tif result == null or not result.is_success():\n\t\treturn report_error(GdAssertMessages.error_result_is_success(result))\n\treturn report_success()\n\n\nfunc is_warning() -> GdUnitResultAssert:\n\tvar result := current_value()\n\tif result == null or not result.is_warn():\n\t\treturn report_error(GdAssertMessages.error_result_is_warning(result))\n\treturn report_success()\n\n\nfunc is_error() -> GdUnitResultAssert:\n\tvar result := current_value()\n\tif result == null or not result.is_error():\n\t\treturn report_error(GdAssertMessages.error_result_is_error(result))\n\treturn report_success()\n\n\nfunc contains_message(expected :String) -> GdUnitResultAssert:\n\tvar result := current_value()\n\tif result == null:\n\t\treturn report_error(GdAssertMessages.error_result_has_message(\"<null>\", expected))\n\tif result.is_success():\n\t\treturn report_error(GdAssertMessages.error_result_has_message_on_success(expected))\n\tif result.is_error() and result.error_message() != expected:\n\t\treturn report_error(GdAssertMessages.error_result_has_message(result.error_message(), expected))\n\tif result.is_warn() and result.warn_message() != expected:\n\t\treturn report_error(GdAssertMessages.error_result_has_message(result.warn_message(), expected))\n\treturn report_success()\n\n\nfunc is_value(expected :Variant) -> GdUnitResultAssert:\n\tvar result := current_value()\n\tvar value :Variant = null if result == null else result.value()\n\tif not GdObjects.equals(value, expected):\n\t\treturn report_error(GdAssertMessages.error_result_is_value(value, expected))\n\treturn report_success()\n\n\nfunc is_equal(expected :Variant) -> GdUnitResultAssert:\n\treturn is_value(expected)\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd.uid",
    "content": "uid://dos1pgw6u4kur\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd",
    "content": "extends GdUnitSignalAssert\n\nconst DEFAULT_TIMEOUT := 2000\n\nvar _signal_collector :GdUnitSignalCollector\nvar _emitter :Object\nvar _current_failure_message :String = \"\"\nvar _custom_failure_message :String = \"\"\nvar _additional_failure_message: String = \"\"\nvar _line_number := -1\nvar _timeout := DEFAULT_TIMEOUT\nvar _interrupted := false\n\n\nfunc _init(emitter :Object) -> void:\n\t# save the actual assert instance on the current thread context\n\tvar context := GdUnitThreadManager.get_current_context()\n\tcontext.set_assert(self)\n\t_signal_collector = context.get_signal_collector()\n\t_line_number = GdUnitAssertions.get_line_number()\n\t_emitter =  emitter\n\tGdAssertReports.reset_last_error_line_number()\n\n\nfunc _notification(what :int) -> void:\n\tif what == NOTIFICATION_PREDELETE:\n\t\t_interrupted = true\n\t\tif is_instance_valid(_emitter):\n\t\t\t_signal_collector.unregister_emitter(_emitter)\n\t\t_emitter = null\n\n\nfunc report_success() -> GdUnitAssert:\n\tGdAssertReports.report_success()\n\treturn self\n\n\nfunc report_warning(message :String) -> GdUnitAssert:\n\tGdAssertReports.report_warning(message, GdUnitAssertions.get_line_number())\n\treturn self\n\n\nfunc report_error(failure :String) -> GdUnitAssert:\n\t_current_failure_message = GdAssertMessages.build_failure_message(failure, _additional_failure_message, _custom_failure_message)\n\tGdAssertReports.report_error(_current_failure_message, _line_number)\n\treturn self\n\n\nfunc failure_message() -> String:\n\treturn _current_failure_message\n\n\nfunc override_failure_message(message :String) -> GdUnitSignalAssert:\n\t_custom_failure_message = message\n\treturn self\n\n\nfunc append_failure_message(message :String) -> GdUnitSignalAssert:\n\t_additional_failure_message = message\n\treturn self\n\n\nfunc wait_until(timeout := 2000) -> GdUnitSignalAssert:\n\tif timeout <= 0:\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\treport_warning(\"Invalid timeout parameter, allowed timeouts must be greater than 0, use default timeout instead!\")\n\t\t_timeout = DEFAULT_TIMEOUT\n\telse:\n\t\t_timeout = timeout\n\treturn self\n\n\n# Verifies the signal exists checked the emitter\nfunc is_signal_exists(signal_name :String) -> GdUnitSignalAssert:\n\tif not _emitter.has_signal(signal_name):\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\treport_error(\"The signal '%s' not exists checked object '%s'.\" % [signal_name, _emitter.get_class()])\n\treturn self\n\n\n# Verifies that given signal is emitted until waiting time\nfunc is_emitted(name :String, args := []) -> GdUnitSignalAssert:\n\t_line_number = GdUnitAssertions.get_line_number()\n\treturn await _wail_until_signal(name, args, false)\n\n\n# Verifies that given signal is NOT emitted until waiting time\nfunc is_not_emitted(name :String, args := []) -> GdUnitSignalAssert:\n\t_line_number = GdUnitAssertions.get_line_number()\n\treturn await _wail_until_signal(name, args, true)\n\n\nfunc _wail_until_signal(signal_name :String, expected_args :Array, expect_not_emitted: bool) -> GdUnitSignalAssert:\n\tif _emitter == null:\n\t\treturn report_error(\"Can't wait for signal checked a NULL object.\")\n\t# first verify the signal is defined\n\tif not _emitter.has_signal(signal_name):\n\t\treturn report_error(\"Can't wait for non-existion signal '%s' checked object '%s'.\" % [signal_name,_emitter.get_class()])\n\t_signal_collector.register_emitter(_emitter)\n\tvar time_scale := Engine.get_time_scale()\n\tvar timer := Timer.new()\n\t(Engine.get_main_loop() as SceneTree).root.add_child(timer)\n\ttimer.add_to_group(\"GdUnitTimers\")\n\ttimer.set_one_shot(true)\n\t@warning_ignore(\"return_value_discarded\")\n\ttimer.timeout.connect(func on_timeout() -> void: _interrupted = true)\n\ttimer.start((_timeout/1000.0)*time_scale)\n\tvar is_signal_emitted := false\n\twhile not _interrupted and not is_signal_emitted:\n\t\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\t\tif is_instance_valid(_emitter):\n\t\t\tis_signal_emitted = _signal_collector.match(_emitter, signal_name, expected_args)\n\t\t\tif is_signal_emitted and expect_not_emitted:\n\t\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t\treport_error(GdAssertMessages.error_signal_emitted(signal_name, expected_args, LocalTime.elapsed(int(_timeout-timer.time_left*1000))))\n\n\tif _interrupted and not expect_not_emitted:\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\treport_error(GdAssertMessages.error_wait_signal(signal_name, expected_args, LocalTime.elapsed(_timeout)))\n\ttimer.free()\n\tif is_instance_valid(_emitter):\n\t\t_signal_collector.reset_received_signals(_emitter, signal_name, expected_args)\n\treturn self\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd.uid",
    "content": "uid://8s5hipbagsfw\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd",
    "content": "extends GdUnitStringAssert\n\nvar _base: GdUnitAssertImpl\n\n\nfunc _init(current :Variant) -> void:\n\t_base = GdUnitAssertImpl.new(current)\n\t# save the actual assert instance on the current thread context\n\tGdUnitThreadManager.get_current_context().set_assert(self)\n\tif current != null and typeof(current) != TYPE_STRING and typeof(current) != TYPE_STRING_NAME:\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\treport_error(\"GdUnitStringAssert inital error, unexpected type <%s>\" % GdObjects.typeof_as_string(current))\n\n\nfunc _notification(event :int) -> void:\n\tif event == NOTIFICATION_PREDELETE:\n\t\tif _base != null:\n\t\t\t_base.notification(event)\n\t\t\t_base = null\n\n\nfunc failure_message() -> String:\n\treturn _base.failure_message()\n\n\nfunc current_value() -> Variant:\n\treturn _base.current_value()\n\n\nfunc report_success() -> GdUnitStringAssert:\n\t_base.report_success()\n\treturn self\n\n\nfunc report_error(error :String) -> GdUnitStringAssert:\n\t_base.report_error(error)\n\treturn self\n\n\nfunc override_failure_message(message :String) -> GdUnitStringAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.override_failure_message(message)\n\treturn self\n\n\nfunc append_failure_message(message :String) -> GdUnitStringAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.append_failure_message(message)\n\treturn self\n\n\nfunc is_null() -> GdUnitStringAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_null()\n\treturn self\n\n\nfunc is_not_null() -> GdUnitStringAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_not_null()\n\treturn self\n\n\nfunc is_equal(expected :Variant) -> GdUnitStringAssert:\n\tvar current :Variant = current_value()\n\tif current == null:\n\t\treturn report_error(GdAssertMessages.error_equal(current, expected))\n\tif not GdObjects.equals(current, expected):\n\t\tvar diffs := GdDiffTool.string_diff(current, expected)\n\t\tvar formatted_current := GdAssertMessages.colored_array_div(diffs[1])\n\t\treturn report_error(GdAssertMessages.error_equal(formatted_current, expected))\n\treturn report_success()\n\n\nfunc is_equal_ignoring_case(expected :Variant) -> GdUnitStringAssert:\n\tvar current :Variant = current_value()\n\tif current == null:\n\t\treturn report_error(GdAssertMessages.error_equal_ignoring_case(current, expected))\n\tif not GdObjects.equals(str(current), expected, true):\n\t\tvar diffs := GdDiffTool.string_diff(current, expected)\n\t\tvar formatted_current := GdAssertMessages.colored_array_div(diffs[1])\n\t\treturn report_error(GdAssertMessages.error_equal_ignoring_case(formatted_current, expected))\n\treturn report_success()\n\n\nfunc is_not_equal(expected :Variant) -> GdUnitStringAssert:\n\tvar current :Variant = current_value()\n\tif GdObjects.equals(current, expected):\n\t\treturn report_error(GdAssertMessages.error_not_equal(current, expected))\n\treturn report_success()\n\n\nfunc is_not_equal_ignoring_case(expected :Variant) -> GdUnitStringAssert:\n\tvar current :Variant = current_value()\n\tif GdObjects.equals(current, expected, true):\n\t\treturn report_error(GdAssertMessages.error_not_equal(current, expected))\n\treturn report_success()\n\n\nfunc is_empty() -> GdUnitStringAssert:\n\tvar current :Variant = current_value()\n\t@warning_ignore(\"unsafe_cast\")\n\tif current == null or not (current as String).is_empty():\n\t\treturn report_error(GdAssertMessages.error_is_empty(current))\n\treturn report_success()\n\n\nfunc is_not_empty() -> GdUnitStringAssert:\n\tvar current :Variant = current_value()\n\t@warning_ignore(\"unsafe_cast\")\n\tif current == null or (current as String).is_empty():\n\t\treturn report_error(GdAssertMessages.error_is_not_empty())\n\treturn report_success()\n\n\nfunc contains(expected :String) -> GdUnitStringAssert:\n\tvar current :Variant = current_value()\n\t@warning_ignore(\"unsafe_cast\")\n\tif current == null or (current as String).find(expected) == -1:\n\t\treturn report_error(GdAssertMessages.error_contains(current, expected))\n\treturn report_success()\n\n\nfunc not_contains(expected :String) -> GdUnitStringAssert:\n\tvar current :Variant = current_value()\n\t@warning_ignore(\"unsafe_cast\")\n\tif current != null and (current as String).find(expected) != -1:\n\t\treturn report_error(GdAssertMessages.error_not_contains(current, expected))\n\treturn report_success()\n\n\nfunc contains_ignoring_case(expected :String) -> GdUnitStringAssert:\n\tvar current :Variant = current_value()\n\t@warning_ignore(\"unsafe_cast\")\n\tif current == null or (current as String).findn(expected) == -1:\n\t\treturn report_error(GdAssertMessages.error_contains_ignoring_case(current, expected))\n\treturn report_success()\n\n\nfunc not_contains_ignoring_case(expected :String) -> GdUnitStringAssert:\n\tvar current :Variant = current_value()\n\t@warning_ignore(\"unsafe_cast\")\n\tif current != null and (current as String).findn(expected) != -1:\n\t\treturn report_error(GdAssertMessages.error_not_contains_ignoring_case(current, expected))\n\treturn report_success()\n\n\nfunc starts_with(expected :String) -> GdUnitStringAssert:\n\tvar current :Variant = current_value()\n\t@warning_ignore(\"unsafe_cast\")\n\tif current == null or (current as String).find(expected) != 0:\n\t\treturn report_error(GdAssertMessages.error_starts_with(current, expected))\n\treturn report_success()\n\n\nfunc ends_with(expected :String) -> GdUnitStringAssert:\n\tvar current :Variant = current_value()\n\tif current == null:\n\t\treturn report_error(GdAssertMessages.error_ends_with(current, expected))\n\t@warning_ignore(\"unsafe_cast\")\n\tvar find :int = (current as String).length() - expected.length()\n\t@warning_ignore(\"unsafe_cast\")\n\tif (current as String).rfind(expected) != find:\n\t\treturn report_error(GdAssertMessages.error_ends_with(current, expected))\n\treturn report_success()\n\n\n# gdlint:disable=max-returns\nfunc has_length(expected :int, comparator := Comparator.EQUAL) -> GdUnitStringAssert:\n\tvar current :Variant = current_value()\n\tif current == null:\n\t\treturn report_error(GdAssertMessages.error_has_length(current, expected, comparator))\n\tvar str_current: String = current\n\tmatch comparator:\n\t\tComparator.EQUAL:\n\t\t\tif str_current.length() != expected:\n\t\t\t\treturn report_error(GdAssertMessages.error_has_length(str_current, expected, comparator))\n\t\tComparator.LESS_THAN:\n\t\t\tif str_current.length() >= expected:\n\t\t\t\treturn report_error(GdAssertMessages.error_has_length(str_current, expected, comparator))\n\t\tComparator.LESS_EQUAL:\n\t\t\tif str_current.length() > expected:\n\t\t\t\treturn report_error(GdAssertMessages.error_has_length(str_current, expected, comparator))\n\t\tComparator.GREATER_THAN:\n\t\t\tif str_current.length() <= expected:\n\t\t\t\treturn report_error(GdAssertMessages.error_has_length(str_current, expected, comparator))\n\t\tComparator.GREATER_EQUAL:\n\t\t\tif str_current.length() < expected:\n\t\t\t\treturn report_error(GdAssertMessages.error_has_length(str_current, expected, comparator))\n\t\t_:\n\t\t\treturn report_error(\"Comparator '%d' not implemented!\" % comparator)\n\treturn report_success()\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd.uid",
    "content": "uid://bnq8nk54ke5hi\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd",
    "content": "extends GdUnitVectorAssert\n\nvar _base: GdUnitAssertImpl\nvar _current_type: int\nvar _type_check: bool\n\nfunc _init(current: Variant, type_check := true) -> void:\n\t_type_check = type_check\n\t_base = GdUnitAssertImpl.new(current)\n\t# save the actual assert instance on the current thread context\n\tGdUnitThreadManager.get_current_context().set_assert(self)\n\tif not _validate_value_type(current):\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\treport_error(\"GdUnitVectorAssert error, the type <%s> is not supported.\" % GdObjects.typeof_as_string(current))\n\t_current_type = typeof(current)\n\n\nfunc _notification(event :int) -> void:\n\tif event == NOTIFICATION_PREDELETE:\n\t\tif _base != null:\n\t\t\t_base.notification(event)\n\t\t\t_base = null\n\n\nfunc _validate_value_type(value :Variant) -> bool:\n\treturn (\n\t\tvalue == null\n\t\tor typeof(value) in [\n\t\t\tTYPE_VECTOR2,\n\t\t\tTYPE_VECTOR2I,\n\t\t\tTYPE_VECTOR3,\n\t\t\tTYPE_VECTOR3I,\n\t\t\tTYPE_VECTOR4,\n\t\t\tTYPE_VECTOR4I\n\t\t]\n\t)\n\n\nfunc _validate_is_vector_type(value :Variant) -> bool:\n\tvar type := typeof(value)\n\tif type == _current_type or _current_type == TYPE_NIL:\n\t\treturn true\n\t@warning_ignore(\"return_value_discarded\")\n\treport_error(GdAssertMessages.error_is_wrong_type(_current_type, type))\n\treturn false\n\n\nfunc current_value() -> Variant:\n\treturn _base.current_value()\n\n\nfunc report_success() -> GdUnitVectorAssert:\n\t_base.report_success()\n\treturn self\n\n\nfunc report_error(error :String) -> GdUnitVectorAssert:\n\t_base.report_error(error)\n\treturn self\n\n\nfunc failure_message() -> String:\n\treturn _base.failure_message()\n\n\nfunc override_failure_message(message :String) -> GdUnitVectorAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.override_failure_message(message)\n\treturn self\n\n\nfunc append_failure_message(message :String) -> GdUnitVectorAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.append_failure_message(message)\n\treturn self\n\n\nfunc is_null() -> GdUnitVectorAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_null()\n\treturn self\n\n\nfunc is_not_null() -> GdUnitVectorAssert:\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_not_null()\n\treturn self\n\n\nfunc is_equal(expected: Variant) -> GdUnitVectorAssert:\n\tif _type_check and not _validate_is_vector_type(expected):\n\t\treturn self\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_equal(expected)\n\treturn self\n\n\nfunc is_not_equal(expected: Variant) -> GdUnitVectorAssert:\n\tif _type_check and not _validate_is_vector_type(expected):\n\t\treturn self\n\t@warning_ignore(\"return_value_discarded\")\n\t_base.is_not_equal(expected)\n\treturn self\n\n\n@warning_ignore(\"shadowed_global_identifier\")\nfunc is_equal_approx(expected :Variant, approx :Variant) -> GdUnitVectorAssert:\n\tif not _validate_is_vector_type(expected) or not _validate_is_vector_type(approx):\n\t\treturn self\n\tvar current :Variant = current_value()\n\tvar from :Variant = expected - approx\n\tvar to :Variant = expected + approx\n\tif current == null or (not _is_equal_approx(current, from, to)):\n\t\treturn report_error(GdAssertMessages.error_is_value(Comparator.BETWEEN_EQUAL, current, from, to))\n\treturn report_success()\n\n\nfunc _is_equal_approx(current :Variant, from :Variant, to :Variant) -> bool:\n\tmatch typeof(current):\n\t\tTYPE_VECTOR2, TYPE_VECTOR2I:\n\t\t\treturn ((current.x >= from.x and current.y >= from.y)\n\t\t\t\tand (current.x <= to.x and current.y <= to.y))\n\t\tTYPE_VECTOR3, TYPE_VECTOR3I:\n\t\t\treturn ((current.x >= from.x and current.y >= from.y and current.z >= from.z)\n\t\t\t\tand (current.x <= to.x and current.y <= to.y and current.z <= to.z))\n\t\tTYPE_VECTOR4, TYPE_VECTOR4I:\n\t\t\treturn ((current.x >= from.x and current.y >= from.y and current.z >= from.z and current.w >= from.w)\n\t\t\t\tand (current.x <= to.x and current.y <= to.y and current.z <= to.z and current.w <= to.w))\n\t\t_:\n\t\t\tpush_error(\"Missing implementation '_is_equal_approx' for vector type %s\" % typeof(current))\n\t\t\treturn false\n\n\nfunc is_less(expected :Variant) -> GdUnitVectorAssert:\n\tif not _validate_is_vector_type(expected):\n\t\treturn self\n\tvar current :Variant = current_value()\n\tif current == null or current >= expected:\n\t\treturn report_error(GdAssertMessages.error_is_value(Comparator.LESS_THAN, current, expected))\n\treturn report_success()\n\n\nfunc is_less_equal(expected :Variant) -> GdUnitVectorAssert:\n\tif not _validate_is_vector_type(expected):\n\t\treturn self\n\tvar current :Variant = current_value()\n\tif current == null or current > expected:\n\t\treturn report_error(GdAssertMessages.error_is_value(Comparator.LESS_EQUAL, current, expected))\n\treturn report_success()\n\n\nfunc is_greater(expected :Variant) -> GdUnitVectorAssert:\n\tif not _validate_is_vector_type(expected):\n\t\treturn self\n\tvar current :Variant = current_value()\n\tif current == null or current <= expected:\n\t\treturn report_error(GdAssertMessages.error_is_value(Comparator.GREATER_THAN, current, expected))\n\treturn report_success()\n\n\nfunc is_greater_equal(expected :Variant) -> GdUnitVectorAssert:\n\tif not _validate_is_vector_type(expected):\n\t\treturn self\n\tvar current :Variant = current_value()\n\tif current == null or current < expected:\n\t\treturn report_error(GdAssertMessages.error_is_value(Comparator.GREATER_EQUAL, current, expected))\n\treturn report_success()\n\n\nfunc is_between(from :Variant, to :Variant) -> GdUnitVectorAssert:\n\tif not _validate_is_vector_type(from) or not _validate_is_vector_type(to):\n\t\treturn self\n\tvar current :Variant = current_value()\n\tif current == null or not (current >= from and current <= to):\n\t\treturn report_error(GdAssertMessages.error_is_value(Comparator.BETWEEN_EQUAL, current, from, to))\n\treturn report_success()\n\n\nfunc is_not_between(from :Variant, to :Variant) -> GdUnitVectorAssert:\n\tif not _validate_is_vector_type(from) or not _validate_is_vector_type(to):\n\t\treturn self\n\tvar current :Variant = current_value()\n\tif (current != null and current >= from and current <= to):\n\t\treturn report_error(GdAssertMessages.error_is_value(Comparator.NOT_BETWEEN_EQUAL, current, from, to))\n\treturn report_success()\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd.uid",
    "content": "uid://cq6iem471uog7\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/ValueProvider.gd",
    "content": "# base interface for assert value provider\nclass_name ValueProvider\nextends RefCounted\n\nfunc get_value() -> Variant:\n\treturn null\n\n\nfunc dispose() -> void:\n\tpass\n"
  },
  {
    "path": "addons/gdUnit4/src/asserts/ValueProvider.gd.uid",
    "content": "uid://bw62dgu2b1aok\n"
  },
  {
    "path": "addons/gdUnit4/src/cmd/CmdArgumentParser.gd",
    "content": "class_name CmdArgumentParser\nextends RefCounted\n\nvar _options :CmdOptions\nvar _tool_name :String\nvar _parsed_commands :Dictionary = Dictionary()\n\n\nfunc _init(p_options :CmdOptions, p_tool_name :String) -> void:\n\t_options = p_options\n\t_tool_name = p_tool_name\n\n\nfunc parse(args :Array, ignore_unknown_cmd := false) -> GdUnitResult:\n\t_parsed_commands.clear()\n\n\t# parse until first program argument\n\twhile not args.is_empty():\n\t\tvar arg :String = args.pop_front()\n\t\tif arg.find(_tool_name) != -1:\n\t\t\tbreak\n\n\tif args.is_empty():\n\t\treturn GdUnitResult.empty()\n\n\t# now parse all arguments\n\twhile not args.is_empty():\n\t\tvar cmd :String = args.pop_front()\n\t\tvar option := _options.get_option(cmd)\n\n\t\tif option:\n\t\t\tif _parse_cmd_arguments(option, args) == -1:\n\t\t\t\treturn GdUnitResult.error(\"The '%s' command requires an argument!\" % option.short_command())\n\t\telif not ignore_unknown_cmd:\n\t\t\treturn GdUnitResult.error(\"Unknown '%s' command!\" % cmd)\n\treturn GdUnitResult.success(_parsed_commands.values())\n\n\nfunc options() -> CmdOptions:\n\treturn _options\n\n\nfunc _parse_cmd_arguments(option: CmdOption, args: Array) -> int:\n\tvar command_name := option.short_command()\n\tvar command: CmdCommand = _parsed_commands.get(command_name, CmdCommand.new(command_name))\n\n\tif option.has_argument():\n\t\tif not option.is_argument_optional() and args.is_empty():\n\t\t\treturn -1\n\t\tif _is_next_value_argument(args):\n\t\t\tvar value: String = args.pop_front()\n\t\t\tcommand.add_argument(value)\n\t\telif not option.is_argument_optional():\n\t\t\treturn -1\n\t_parsed_commands[command_name] = command\n\treturn 0\n\n\nfunc _is_next_value_argument(args: PackedStringArray) -> bool:\n\tif args.is_empty():\n\t\treturn false\n\treturn _options.get_option(args[0]) == null\n"
  },
  {
    "path": "addons/gdUnit4/src/cmd/CmdArgumentParser.gd.uid",
    "content": "uid://bos3yyqltyx8a\n"
  },
  {
    "path": "addons/gdUnit4/src/cmd/CmdCommand.gd",
    "content": "class_name CmdCommand\nextends RefCounted\n\nvar _name: String\nvar _arguments: PackedStringArray\n\n\nfunc _init(p_name :String, p_arguments := []) -> void:\n\t_name = p_name\n\t_arguments = PackedStringArray(p_arguments)\n\n\nfunc name() -> String:\n\treturn _name\n\n\nfunc arguments() -> PackedStringArray:\n\treturn _arguments\n\n\nfunc add_argument(arg :String) -> void:\n\t@warning_ignore(\"return_value_discarded\")\n\t_arguments.append(arg)\n\n\nfunc _to_string() -> String:\n\treturn \"%s:%s\" % [_name, \", \".join(_arguments)]\n"
  },
  {
    "path": "addons/gdUnit4/src/cmd/CmdCommand.gd.uid",
    "content": "uid://drrvmtivvquyj\n"
  },
  {
    "path": "addons/gdUnit4/src/cmd/CmdCommandHandler.gd",
    "content": "class_name CmdCommandHandler\nextends RefCounted\n\nconst CB_SINGLE_ARG = 0\nconst CB_MULTI_ARGS = 1\nconst NO_CB := Callable()\n\nvar _cmd_options :CmdOptions\n# holds the command callbacks by key:<cmd_name>:String and value: [<cb single arg>, <cb multible args>]:Array\n# Dictionary[String, Array[Callback]\nvar _command_cbs :Dictionary\n\n\n\nfunc _init(cmd_options: CmdOptions) -> void:\n\t_cmd_options = cmd_options\n\n\n# register a callback function for given command\n# cmd_name short name of the command\n# fr_arg a funcref to a function with a single argument\nfunc register_cb(cmd_name: String, cb: Callable) -> CmdCommandHandler:\n\tvar registered_cb: Array = _command_cbs.get(cmd_name, [NO_CB, NO_CB])\n\tif registered_cb[CB_SINGLE_ARG]:\n\t\tpush_error(\"A function for command '%s' is already registered!\" % cmd_name)\n\t\treturn self\n\n\tif not _validate_cb_signature(cb, TYPE_STRING):\n\t\tpush_error(\n\t\t\t(\"The callback '%s:%s' for command '%s' has invalid function signature. \"\n\t\t\t+\"The callback signature must be 'func name(value: PackedStringArray)'\")\n\t\t\t% [cb.get_object().get_class(), cb.get_method(), cmd_name])\n\t\treturn null\n\n\tregistered_cb[CB_SINGLE_ARG] = cb\n\t_command_cbs[cmd_name] = registered_cb\n\treturn self\n\n\n# register a callback function for given command\n# cb a funcref to a function with a variable number of arguments but expects all parameters to be passed via a single Array.\nfunc register_cbv(cmd_name: String, cb: Callable) -> CmdCommandHandler:\n\tvar registered_cb: Array = _command_cbs.get(cmd_name, [NO_CB, NO_CB])\n\tif registered_cb[CB_MULTI_ARGS]:\n\t\tpush_error(\"A function for command '%s' is already registered!\" % cmd_name)\n\t\treturn self\n\n\tif not _validate_cb_signature(cb, TYPE_PACKED_STRING_ARRAY):\n\t\tpush_error(\n\t\t\t(\"The callback '%s:%s' for command '%s' has invalid function signature. \"\n\t\t\t+\"The callback signature must be 'func name(value: PackedStringArray)'\")\n\t\t\t% [cb.get_object().get_class(), cb.get_method(), cmd_name])\n\t\treturn null\n\n\tregistered_cb[CB_MULTI_ARGS] = cb\n\t_command_cbs[cmd_name] = registered_cb\n\treturn self\n\n\nfunc _validate() -> GdUnitResult:\n\tvar errors := PackedStringArray()\n\t# Dictionary[StringName, String]\n\tvar registered_cbs := Dictionary()\n\n\tfor cmd_name in _command_cbs.keys() as Array[String]:\n\t\tvar cb: Callable = (_command_cbs[cmd_name][CB_SINGLE_ARG]\n\t\t\tif _command_cbs[cmd_name][CB_SINGLE_ARG]\n\t\t\telse _command_cbs[cmd_name][CB_MULTI_ARGS])\n\t\tif cb != NO_CB and not cb.is_valid():\n\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\terrors.append(\"Invalid function reference for command '%s', Check the function reference!\" % cmd_name)\n\t\tif _cmd_options.get_option(cmd_name) == null:\n\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\terrors.append(\"The command '%s' is unknown, verify your CmdOptions!\" % cmd_name)\n\t\t# verify for multiple registered command callbacks\n\t\tif cb != NO_CB:\n\t\t\tvar cb_method := cb.get_method()\n\t\t\tif registered_cbs.has(cb_method):\n\t\t\t\tvar already_registered_cmd :String = registered_cbs[cb_method]\n\t\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t\terrors.append(\"The function reference '%s' already registerd for command '%s'!\" % [cb_method, already_registered_cmd])\n\t\t\telse:\n\t\t\t\tregistered_cbs[cb_method] = cmd_name\n\tif errors.is_empty():\n\t\treturn GdUnitResult.success(true)\n\treturn GdUnitResult.error(\"\\n\".join(errors))\n\n\nfunc execute(commands: Array[CmdCommand]) -> GdUnitResult:\n\tvar result := _validate()\n\tif result.is_error():\n\t\treturn result\n\tfor cmd in commands:\n\t\tvar cmd_name := cmd.name()\n\t\tif _command_cbs.has(cmd_name):\n\t\t\tvar cb_s: Callable = _command_cbs.get(cmd_name)[CB_SINGLE_ARG]\n\t\t\tvar arguments := cmd.arguments()\n\t\t\tvar cmd_option := _cmd_options.get_option(cmd_name)\n\n\t\t\tif arguments.is_empty():\n\t\t\t\tcb_s.call()\n\t\t\telif arguments.size() > 1:\n\t\t\t\tvar cb_m: Callable = _command_cbs.get(cmd_name)[CB_MULTI_ARGS]\n\t\t\t\tcb_m.call(arguments)\n\t\t\telse:\n\t\t\t\tif cmd_option.type() == TYPE_BOOL:\n\t\t\t\t\tcb_s.call(true if arguments[0] == \"true\" else false)\n\t\t\t\telse:\n\t\t\t\t\tcb_s.call(arguments[0])\n\n\treturn GdUnitResult.success(true)\n\n\nfunc _validate_cb_signature(cb: Callable, arg_type: int) -> bool:\n\tfor m in cb.get_object().get_method_list():\n\t\tif m[\"name\"] == cb.get_method():\n\t\t\t@warning_ignore(\"unsafe_cast\")\n\t\t\treturn _validate_func_arguments(m[\"args\"] as Array, arg_type)\n\treturn true\n\n\nfunc _validate_func_arguments(arguments: Array, arg_type: int) -> bool:\n\t# validate we have a single argument\n\tif arguments.size() > 1:\n\t\treturn false\n\t# a cb with no arguments is also valid\n\tif arguments.size() == 0:\n\t\treturn true\n\t# validate argument type\n\tvar arg: Dictionary = arguments[0]\n\t@warning_ignore(\"unsafe_cast\")\n\tif arg[\"usage\"] as int == PROPERTY_USAGE_NIL_IS_VARIANT:\n\t\treturn true\n\tif arg[\"type\"] != arg_type:\n\t\treturn false\n\treturn true\n"
  },
  {
    "path": "addons/gdUnit4/src/cmd/CmdCommandHandler.gd.uid",
    "content": "uid://uo2m211sso00\n"
  },
  {
    "path": "addons/gdUnit4/src/cmd/CmdConsole.gd",
    "content": "# prototype of console with CSI support\n# https://notes.burke.libbey.me/ansi-escape-codes/\nclass_name CmdConsole\nextends RefCounted\n\nenum {\n\tCOLOR_TABLE,\n\tCOLOR_RGB\n}\n\nconst BOLD = 0x1\nconst ITALIC = 0x2\nconst UNDERLINE = 0x4\n\nconst CSI_BOLD = \"\u001b[1m\"\nconst CSI_ITALIC = \"\u001b[3m\"\nconst CSI_UNDERLINE = \"\u001b[4m\"\n\n# Control Sequence Introducer\nvar _debug_show_color_codes := false\nvar _color_mode := COLOR_TABLE\n\n\nfunc color(p_color :Color) -> CmdConsole:\n\t# using color table 16 - 231 a  6 x 6 x 6 RGB color cube  (16 + R * 36 + G * 6 + B)\n\t#if _color_mode == COLOR_TABLE:\n\t#\t@warning_ignore(\"integer_division\")\n\t#\tvar c2 := 16 + (int(p_color.r8/42) * 36) + (int(p_color.g8/42) * 6) + int(p_color.b8/42)\n\t#\tif _debug_show_color_codes:\n\t#\t\tprintraw(\"%6d\" % [c2])\n\t#\tprintraw(\"\u001b[38;5;%dm\" % c2 )\n\t#else:\n\tprintraw(\"\u001b[38;2;%d;%d;%dm\" % [p_color.r8, p_color.g8, p_color.b8] )\n\treturn self\n\n\nfunc save_cursor() -> CmdConsole:\n\tprintraw(\"\u001b[s\")\n\treturn self\n\n\nfunc restore_cursor() -> CmdConsole:\n\tprintraw(\"\u001b[u\")\n\treturn self\n\n\nfunc end_color() -> CmdConsole:\n\tprintraw(\"\u001b[0m\")\n\treturn self\n\n\nfunc row_pos(row :int) -> CmdConsole:\n\tprintraw(\"\u001b[%d;0H\" % row )\n\treturn self\n\n\nfunc scroll_area(from :int, to :int) -> CmdConsole:\n\tprintraw(\"\u001b[%d;%dr\" % [from ,to])\n\treturn self\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc progress_bar(p_progress :int, p_color :Color = Color.POWDER_BLUE) -> CmdConsole:\n\tif p_progress < 0:\n\t\tp_progress = 0\n\tif p_progress > 100:\n\t\tp_progress = 100\n\tcolor(p_color)\n\tprintraw(\"[%-50s] %-3d%%\\r\" % [\"\".lpad(int(p_progress/2.0), \"■\").rpad(50, \"-\"), p_progress])\n\tend_color()\n\treturn self\n\n\nfunc printl(value :String) -> CmdConsole:\n\tprintraw(value)\n\treturn self\n\n\nfunc new_line() -> CmdConsole:\n\tprints()\n\treturn self\n\n\nfunc reset() -> CmdConsole:\n\treturn self\n\n\nfunc bold(enable :bool) -> CmdConsole:\n\tif enable:\n\t\tprintraw(CSI_BOLD)\n\treturn self\n\n\nfunc italic(enable :bool) -> CmdConsole:\n\tif enable:\n\t\tprintraw(CSI_ITALIC)\n\treturn self\n\n\nfunc underline(enable :bool) -> CmdConsole:\n\tif enable:\n\t\tprintraw(CSI_UNDERLINE)\n\treturn self\n\n\nfunc prints_error(message :String) -> CmdConsole:\n\treturn color(Color.CRIMSON).printl(message).end_color().new_line()\n\n\nfunc prints_warning(message :String) -> CmdConsole:\n\treturn color(Color.GOLDENROD).printl(message).end_color().new_line()\n\n\nfunc prints_color(p_message :String, p_color :Color, p_flags := 0) -> CmdConsole:\n\treturn print_color(p_message, p_color, p_flags).new_line()\n\n\nfunc print_color(p_message :String, p_color :Color, p_flags := 0) -> CmdConsole:\n\treturn color(p_color)\\\n\t\t.bold(p_flags&BOLD == BOLD)\\\n\t\t.italic(p_flags&ITALIC == ITALIC)\\\n\t\t.underline(p_flags&UNDERLINE == UNDERLINE)\\\n\t\t.printl(p_message)\\\n\t\t.end_color()\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc print_color_table() -> void:\n\tprints_color(\"Color Table 6x6x6\", Color.ANTIQUE_WHITE)\n\t_debug_show_color_codes = true\n\tfor green in range(0, 6):\n\t\tfor red in range(0, 6):\n\t\t\tfor blue in range(0, 6):\n\t\t\t\tprint_color(\"████████ \", Color8(red*42, green*42, blue*42))\n\t\t\tnew_line()\n\t\tnew_line()\n\n\tprints_color(\"Color Table RGB\", Color.ANTIQUE_WHITE)\n\t_color_mode = COLOR_RGB\n\tfor green in range(0, 6):\n\t\tfor red in range(0, 6):\n\t\t\tfor blue in range(0, 6):\n\t\t\t\tprint_color(\"████████ \", Color8(red*42, green*42, blue*42))\n\t\t\tnew_line()\n\t\tnew_line()\n\t_color_mode = COLOR_TABLE\n\t_debug_show_color_codes = false\n"
  },
  {
    "path": "addons/gdUnit4/src/cmd/CmdConsole.gd.uid",
    "content": "uid://1w10f5i1ybih\n"
  },
  {
    "path": "addons/gdUnit4/src/cmd/CmdOption.gd",
    "content": "class_name CmdOption\nextends RefCounted\n\n\nvar _commands :PackedStringArray\nvar _help :String\nvar _description :String\nvar _type :int\nvar _arg_optional :bool = false\n\n\n# constructs a command option by given arguments\n# commands : a string with comma separated list of available commands begining with the short form\n# help: a help text show howto use\n# description: a full description of the command\n# type: the argument type\n# arg_optional: defines of the argument optional\nfunc _init(p_commands :String, p_help :String, p_description :String, p_type :int = TYPE_NIL, p_arg_optional :bool = false) -> void:\n\t_commands = p_commands.replace(\" \", \"\").replace(\"\\t\", \"\").split(\",\")\n\t_help = p_help\n\t_description = p_description\n\t_type = p_type\n\t_arg_optional = p_arg_optional\n\n\nfunc commands() -> PackedStringArray:\n\treturn _commands\n\n\nfunc short_command() -> String:\n\treturn _commands[0]\n\n\nfunc help() -> String:\n\treturn _help\n\n\nfunc description() -> String:\n\treturn _description\n\n\nfunc type() -> int:\n\treturn _type\n\n\nfunc is_argument_optional() -> bool:\n\treturn _arg_optional\n\n\nfunc has_argument() -> bool:\n\treturn _type != TYPE_NIL\n\n\nfunc describe() -> String:\n\tif help().is_empty():\n\t\treturn \"  %-32s %s \\n\" % [commands(), description()]\n\treturn \"  %-32s %s \\n  %-32s %s\\n\" % [commands(), description(), \"\", help()]\n\n\nfunc _to_string() -> String:\n\treturn describe()\n"
  },
  {
    "path": "addons/gdUnit4/src/cmd/CmdOption.gd.uid",
    "content": "uid://4xs88x0hyv5b\n"
  },
  {
    "path": "addons/gdUnit4/src/cmd/CmdOptions.gd",
    "content": "class_name CmdOptions\nextends RefCounted\n\n\nvar _default_options :Array[CmdOption]\nvar _advanced_options :Array[CmdOption]\n\n\nfunc _init(p_options :Array[CmdOption] = [], p_advanced_options :Array[CmdOption] = []) -> void:\n\t# default help options\n\t_default_options = p_options\n\t_advanced_options = p_advanced_options\n\n\nfunc default_options() -> Array[CmdOption]:\n\treturn _default_options\n\n\nfunc advanced_options() -> Array[CmdOption]:\n\treturn _advanced_options\n\n\nfunc options() -> Array[CmdOption]:\n\treturn default_options() + advanced_options()\n\n\nfunc get_option(cmd :String) -> CmdOption:\n\tfor option in options():\n\t\tif Array(option.commands()).has(cmd):\n\t\t\treturn option\n\treturn null\n"
  },
  {
    "path": "addons/gdUnit4/src/cmd/CmdOptions.gd.uid",
    "content": "uid://42l0nbnkkbtk\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdArrayTools.gd",
    "content": "## Small helper tool to work with Godot Arrays\nclass_name GdArrayTools\nextends RefCounted\n\n\nconst max_elements := 32\nconst ARRAY_TYPES := [\n\tTYPE_ARRAY,\n\tTYPE_PACKED_BYTE_ARRAY,\n\tTYPE_PACKED_INT32_ARRAY,\n\tTYPE_PACKED_INT64_ARRAY,\n\tTYPE_PACKED_FLOAT32_ARRAY,\n\tTYPE_PACKED_FLOAT64_ARRAY,\n\tTYPE_PACKED_STRING_ARRAY,\n\tTYPE_PACKED_VECTOR2_ARRAY,\n\tTYPE_PACKED_VECTOR3_ARRAY,\n\tTYPE_PACKED_COLOR_ARRAY\n]\n\n\nstatic func is_array_type(value :Variant) -> bool:\n\treturn is_type_array(typeof(value))\n\n\nstatic func is_type_array(type :int) -> bool:\n\treturn  type in ARRAY_TYPES\n\n\n## Filters an array by given value[br]\n## If the given value not an array it returns null, will remove all occurence of given value.\n@warning_ignore(\"unsafe_method_access\")\nstatic func filter_value(array: Variant, value: Variant) -> Variant:\n\tif not is_array_type(array):\n\t\treturn null\n\tvar filtered_array: Variant = array.duplicate()\n\tvar index :int = filtered_array.find(value)\n\twhile index != -1:\n\t\tfiltered_array.remove_at(index)\n\t\tindex = filtered_array.find(value)\n\treturn filtered_array\n\n\n## Erases a value from given array by using equals(l,r) to find the element to erase\nstatic func erase_value(array :Array, value :Variant) -> void:\n\tfor element :Variant in array:\n\t\tif GdObjects.equals(element, value):\n\t\t\tarray.erase(element)\n\n\n## Scans for the array build in type on a untyped array[br]\n## Returns the buildin type by scan all values and returns the type if all values has the same type.\n## If the values has different types TYPE_VARIANT is returend\nstatic func scan_typed(array :Array) -> int:\n\tif array.is_empty():\n\t\treturn TYPE_NIL\n\tvar actual_type := GdObjects.TYPE_VARIANT\n\tfor value :Variant in array:\n\t\tvar current_type := typeof(value)\n\t\tif not actual_type in [GdObjects.TYPE_VARIANT, current_type]:\n\t\t\treturn GdObjects.TYPE_VARIANT\n\t\tactual_type = current_type\n\treturn actual_type\n\n\n## Converts given array into a string presentation.[br]\n## This function is different to the original Godot str(<array>) implementation.\n## The string presentaion contains fullquallified typed informations.\n##[br]\n## Examples:\n## \t[codeblock]\n##\t\t# will result in PackedString([\"a\", \"b\"])\n## \t\tGdArrayTools.as_string(PackedStringArray(\"a\", \"b\"))\n##\t\t# will result in PackedString([\"a\", \"b\"])\n##\t\tGdArrayTools.as_string(PackedColorArray(Color.RED, COLOR.GREEN))\n## \t[/codeblock]\nstatic func as_string(elements: Variant, encode_value := true) -> String:\n\tvar delemiter := \", \"\n\tif elements == null:\n\t\treturn \"<null>\"\n\t@warning_ignore(\"unsafe_cast\")\n\tif (elements as Array).is_empty():\n\t\treturn \"<empty>\"\n\tvar prefix := _typeof_as_string(elements) if encode_value else \"\"\n\tvar formatted := \"\"\n\tvar index := 0\n\tfor element :Variant in elements:\n\t\tif max_elements != -1 and index > max_elements:\n\t\t\treturn prefix + \"[\" + formatted + delemiter + \"...]\"\n\t\tif formatted.length() > 0 :\n\t\t\tformatted += delemiter\n\t\tformatted += GdDefaultValueDecoder.decode(element) if encode_value else str(element)\n\t\tindex += 1\n\treturn prefix + \"[\" + formatted + \"]\"\n\n\nstatic func has_same_content(current: Array, other: Array) -> bool:\n\tif current.size() != other.size(): return false\n\tfor element: Variant in current:\n\t\tif not other.has(element): return false\n\t\tif current.count(element) != other.count(element): return false\n\treturn true\n\n\nstatic func _typeof_as_string(value :Variant) -> String:\n\tvar type := typeof(value)\n\t# for untyped array we retun empty string\n\tif type == TYPE_ARRAY:\n\t\treturn \"\"\n\treturn GdObjects.typeof_as_string(value)\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdArrayTools.gd.uid",
    "content": "uid://dy4au3f07f8wa\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdDiffTool.gd",
    "content": "# A tool to find differences between two objects\nclass_name GdDiffTool\nextends RefCounted\n\n\nconst DIV_ADD :int = 214\nconst DIV_SUB :int = 215\n\n\nstatic func _diff(lb: PackedByteArray, rb: PackedByteArray, lookup: Array[Array], ldiff: Array, rdiff: Array) -> void:\n\tvar loffset := lb.size()\n\tvar roffset := rb.size()\n\n\twhile true:\n\t\t#if last character of X and Y matches\n\t\tif loffset > 0 && roffset > 0 && lb[loffset - 1] == rb[roffset - 1]:\n\t\t\tloffset -= 1\n\t\t\troffset -= 1\n\t\t\tldiff.push_front(lb[loffset])\n\t\t\trdiff.push_front(rb[roffset])\n\t\t\tcontinue\n\t\t#current character of Y is not present in X\n\t\telse: if (roffset > 0 && (loffset == 0 || lookup[loffset][roffset - 1] >= lookup[loffset - 1][roffset])):\n\t\t\troffset -= 1\n\t\t\tldiff.push_front(rb[roffset])\n\t\t\tldiff.push_front(DIV_ADD)\n\t\t\trdiff.push_front(rb[roffset])\n\t\t\trdiff.push_front(DIV_SUB)\n\t\t\tcontinue\n\t\t#current character of X is not present in Y\n\t\telse: if (loffset > 0 && (roffset == 0 || lookup[loffset][roffset - 1] < lookup[loffset - 1][roffset])):\n\t\t\tloffset -= 1\n\t\t\tldiff.push_front(lb[loffset])\n\t\t\tldiff.push_front(DIV_SUB)\n\t\t\trdiff.push_front(lb[loffset])\n\t\t\trdiff.push_front(DIV_ADD)\n\t\t\tcontinue\n\t\tbreak\n\n\n# lookup[i][j] stores the length of LCS of substring X[0..i-1], Y[0..j-1]\nstatic func _createLookUp(lb: PackedByteArray, rb: PackedByteArray) -> Array[Array]:\n\tvar lookup: Array[Array] = []\n\t@warning_ignore(\"return_value_discarded\")\n\tlookup.resize(lb.size() + 1)\n\tfor i in lookup.size():\n\t\tvar x := []\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tx.resize(rb.size() + 1)\n\t\tlookup[i] = x\n\treturn lookup\n\n\nstatic func _buildLookup(lb: PackedByteArray, rb: PackedByteArray) -> Array[Array]:\n\tvar lookup := _createLookUp(lb, rb)\n\t# first column of the lookup table will be all 0\n\tfor i in lookup.size():\n\t\tlookup[i][0] = 0\n\t# first row of the lookup table will be all 0\n\tfor j :int in lookup[0].size():\n\t\tlookup[0][j] = 0\n\n\t# fill the lookup table in bottom-up manner\n\tfor i in range(1, lookup.size()):\n\t\tfor j in range(1, lookup[0].size()):\n\t\t\t# if current character of left and right matches\n\t\t\tif lb[i - 1] == rb[j - 1]:\n\t\t\t\tlookup[i][j] = lookup[i - 1][j - 1] + 1;\n\t\t\t# else if current character of left and right don't match\n\t\t\telse:\n\t\t\t\tlookup[i][j] = max(lookup[i - 1][j], lookup[i][j - 1]);\n\treturn lookup\n\n\nstatic func string_diff(left :Variant, right :Variant) -> Array[PackedByteArray]:\n\tvar lb := PackedByteArray() if left == null else str(left).to_utf8_buffer()\n\tvar rb := PackedByteArray() if right == null else str(right).to_utf8_buffer()\n\tvar ldiff := Array()\n\tvar rdiff := Array()\n\tvar lookup := _buildLookup(lb, rb);\n\t_diff(lb, rb, lookup, ldiff, rdiff)\n\treturn [PackedByteArray(ldiff), PackedByteArray(rdiff)]\n\n\n# prototype\nstatic func longestCommonSubsequence(text1 :String, text2 :String) -> PackedStringArray:\n\tvar text1Words := text1.split(\" \")\n\tvar text2Words := text2.split(\" \")\n\tvar text1WordCount := text1Words.size()\n\tvar text2WordCount := text2Words.size()\n\tvar solutionMatrix := Array()\n\tfor i in text1WordCount+1:\n\t\tvar ar := Array()\n\t\tfor n in text2WordCount+1:\n\t\t\tar.append(0)\n\t\tsolutionMatrix.append(ar)\n\n\tfor i in range(text1WordCount-1, 0, -1):\n\t\tfor j in range(text2WordCount-1, 0, -1):\n\t\t\tif text1Words[i] == text2Words[j]:\n\t\t\t\tsolutionMatrix[i][j] = solutionMatrix[i + 1][j + 1] + 1;\n\t\t\telse:\n\t\t\t\tsolutionMatrix[i][j] = max(solutionMatrix[i + 1][j], solutionMatrix[i][j + 1]);\n\n\tvar i := 0\n\tvar j := 0\n\tvar lcsResultList := PackedStringArray();\n\twhile (i < text1WordCount && j < text2WordCount):\n\t\tif text1Words[i] == text2Words[j]:\n\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\tlcsResultList.append(text2Words[j])\n\t\t\ti += 1\n\t\t\tj += 1\n\t\telse: if (solutionMatrix[i + 1][j] >= solutionMatrix[i][j + 1]):\n\t\t\ti += 1\n\t\telse:\n\t\t\tj += 1\n\treturn lcsResultList\n\n\nstatic func markTextDifferences(text1 :String, text2 :String, lcsList :PackedStringArray, insertColor :Color, deleteColor:Color) -> String:\n\tvar stringBuffer := \"\"\n\tif text1 == null and lcsList == null:\n\t\treturn stringBuffer\n\n\tvar text1Words := text1.split(\" \")\n\tvar text2Words := text2.split(\" \")\n\tvar i := 0\n\tvar j := 0\n\tvar word1LastIndex := 0\n\tvar word2LastIndex := 0\n\tfor k in lcsList.size():\n\t\twhile i < text1Words.size() and j < text2Words.size():\n\t\t\tif text1Words[i] == lcsList[k] and text2Words[j] == lcsList[k]:\n\t\t\t\tstringBuffer += \"<SPAN>\" + lcsList[k] + \" </SPAN>\"\n\t\t\t\tword1LastIndex = i + 1\n\t\t\t\tword2LastIndex = j + 1\n\t\t\t\ti = text1Words.size()\n\t\t\t\tj = text2Words.size()\n\n\t\t\telse: if text1Words[i] != lcsList[k]:\n\t\t\t\twhile i < text1Words.size() and text1Words[i] != lcsList[k]:\n\t\t\t\t\tstringBuffer += \"<SPAN style='BACKGROUND-COLOR:\" + deleteColor.to_html() + \"'>\" + text1Words[i] + \" </SPAN>\"\n\t\t\t\t\ti += 1\n\t\t\telse: if text2Words[j] != lcsList[k]:\n\t\t\t\twhile j < text2Words.size() and text2Words[j] != lcsList[k]:\n\t\t\t\t\tstringBuffer += \"<SPAN style='BACKGROUND-COLOR:\" + insertColor.to_html() + \"'>\" + text2Words[j] + \" </SPAN>\"\n\t\t\t\t\tj += 1\n\t\t\ti = word1LastIndex\n\t\t\tj = word2LastIndex\n\n\t\t\twhile word1LastIndex < text1Words.size():\n\t\t\t\tstringBuffer += \"<SPAN style='BACKGROUND-COLOR:\" + deleteColor.to_html() + \"'>\" + text1Words[word1LastIndex] + \" </SPAN>\"\n\t\t\t\tword1LastIndex += 1\n\t\t\twhile word2LastIndex < text2Words.size():\n\t\t\t\tstringBuffer += \"<SPAN style='BACKGROUND-COLOR:\" + insertColor.to_html() + \"'>\" + text2Words[word2LastIndex] + \" </SPAN>\"\n\t\t\t\tword2LastIndex += 1\n\treturn stringBuffer\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdDiffTool.gd.uid",
    "content": "uid://b16qmpeok3it5\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdFunctionDoubler.gd",
    "content": "class_name GdFunctionDoubler\nextends RefCounted\n\nconst DEFAULT_TYPED_RETURN_VALUES := {\n\tTYPE_NIL: \"null\",\n\tTYPE_BOOL: \"false\",\n\tTYPE_INT: \"0\",\n\tTYPE_FLOAT: \"0.0\",\n\tTYPE_STRING: \"\\\"\\\"\",\n\tTYPE_STRING_NAME: \"&\\\"\\\"\",\n\tTYPE_VECTOR2: \"Vector2.ZERO\",\n\tTYPE_VECTOR2I: \"Vector2i.ZERO\",\n\tTYPE_RECT2: \"Rect2()\",\n\tTYPE_RECT2I: \"Rect2i()\",\n\tTYPE_VECTOR3: \"Vector3.ZERO\",\n\tTYPE_VECTOR3I: \"Vector3i.ZERO\",\n\tTYPE_VECTOR4: \"Vector4.ZERO\",\n\tTYPE_VECTOR4I: \"Vector4i.ZERO\",\n\tTYPE_TRANSFORM2D: \"Transform2D()\",\n\tTYPE_PLANE: \"Plane()\",\n\tTYPE_QUATERNION: \"Quaternion()\",\n\tTYPE_AABB: \"AABB()\",\n\tTYPE_BASIS: \"Basis()\",\n\tTYPE_TRANSFORM3D: \"Transform3D()\",\n\tTYPE_PROJECTION: \"Projection()\",\n\tTYPE_COLOR: \"Color()\",\n\tTYPE_NODE_PATH: \"NodePath()\",\n\tTYPE_RID: \"RID()\",\n\tTYPE_OBJECT: \"null\",\n\tTYPE_CALLABLE: \"Callable()\",\n\tTYPE_SIGNAL: \"Signal()\",\n\tTYPE_DICTIONARY: \"Dictionary()\",\n\tTYPE_ARRAY: \"Array()\",\n\tTYPE_PACKED_BYTE_ARRAY: \"PackedByteArray()\",\n\tTYPE_PACKED_INT32_ARRAY: \"PackedInt32Array()\",\n\tTYPE_PACKED_INT64_ARRAY: \"PackedInt64Array()\",\n\tTYPE_PACKED_FLOAT32_ARRAY: \"PackedFloat32Array()\",\n\tTYPE_PACKED_FLOAT64_ARRAY: \"PackedFloat64Array()\",\n\tTYPE_PACKED_STRING_ARRAY: \"PackedStringArray()\",\n\tTYPE_PACKED_VECTOR2_ARRAY: \"PackedVector2Array()\",\n\tTYPE_PACKED_VECTOR3_ARRAY: \"PackedVector3Array()\",\n\t# since Godot 4.3.beta1 TYPE_PACKED_VECTOR4_ARRAY = 38\n\tGdObjects.TYPE_PACKED_VECTOR4_ARRAY: \"PackedVector4Array()\",\n\tTYPE_PACKED_COLOR_ARRAY: \"PackedColorArray()\",\n\tGdObjects.TYPE_VARIANT: \"null\",\n\tGdObjects.TYPE_ENUM: \"0\"\n}\n\n# @GlobalScript enums\n# needs to manually map because of https://github.com/godotengine/godot/issues/73835\nconst DEFAULT_ENUM_RETURN_VALUES = {\n\t\"Side\" : \"SIDE_LEFT\",\n\t\"Corner\" : \"CORNER_TOP_LEFT\",\n\t\"Orientation\" : \"HORIZONTAL\",\n\t\"ClockDirection\" : \"CLOCKWISE\",\n\t\"HorizontalAlignment\" : \"HORIZONTAL_ALIGNMENT_LEFT\",\n\t\"VerticalAlignment\" : \"VERTICAL_ALIGNMENT_TOP\",\n\t\"InlineAlignment\" : \"INLINE_ALIGNMENT_TOP_TO\",\n\t\"EulerOrder\" : \"EULER_ORDER_XYZ\",\n\t\"Key\" : \"KEY_NONE\",\n\t\"KeyModifierMask\" : \"KEY_CODE_MASK\",\n\t\"MouseButton\" : \"MOUSE_BUTTON_NONE\",\n\t\"MouseButtonMask\" : \"MOUSE_BUTTON_MASK_LEFT\",\n\t\"JoyButton\" : \"JOY_BUTTON_INVALID\",\n\t\"JoyAxis\" : \"JOY_AXIS_INVALID\",\n\t\"MIDIMessage\" : \"MIDI_MESSAGE_NONE\",\n\t\"Error\" : \"OK\",\n\t\"PropertyHint\" : \"PROPERTY_HINT_NONE\",\n\t\"Variant.Type\" : \"TYPE_NIL\",\n}\n\nvar _push_errors :String\n\n\n# Determine the enum default by reflection\nstatic func get_enum_default(value :String) -> Variant:\n\tvar script := GDScript.new()\n\tscript.source_code = \"\"\"\n\textends Resource\n\n\tstatic func get_enum_default() -> Variant:\n\t\treturn %s.values()[0]\n\n\t\"\"\".dedent() % value\n\t@warning_ignore(\"return_value_discarded\")\n\tscript.reload()\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn script.new().call(\"get_enum_default\")\n\n\nstatic func default_return_value(func_descriptor :GdFunctionDescriptor) -> String:\n\tvar return_type :Variant = func_descriptor.return_type()\n\tif return_type == GdObjects.TYPE_ENUM:\n\t\tvar enum_class := func_descriptor._return_class\n\t\tvar enum_path := enum_class.split(\".\")\n\t\tif enum_path.size() >= 2:\n\t\t\tvar keys := ClassDB.class_get_enum_constants(enum_path[0], enum_path[1])\n\t\t\tif not keys.is_empty():\n\t\t\t\treturn \"%s.%s\" % [enum_path[0], keys[0]]\n\t\t\tvar enum_value :Variant = get_enum_default(enum_class)\n\t\t\tif enum_value != null:\n\t\t\t\treturn str(enum_value)\n\t\t# we need fallback for @GlobalScript enums,\n\t\treturn DEFAULT_ENUM_RETURN_VALUES.get(func_descriptor._return_class, \"0\")\n\treturn DEFAULT_TYPED_RETURN_VALUES.get(return_type, \"invalid\")\n\n\nfunc _init(push_errors :bool = false) -> void:\n\t_push_errors = \"true\" if push_errors else \"false\"\n\tfor type_key in TYPE_MAX:\n\t\tif not DEFAULT_TYPED_RETURN_VALUES.has(type_key):\n\t\t\tpush_error(\"missing default definitions! Expexting %d bud is %d\" % [DEFAULT_TYPED_RETURN_VALUES.size(), TYPE_MAX])\n\t\t\tprints(\"missing default definition for type\", type_key)\n\t\t\tassert(DEFAULT_TYPED_RETURN_VALUES.has(type_key), \"Missing Type default definition!\")\n\n\n@warning_ignore(\"unused_parameter\")\nfunc get_template(return_type: GdFunctionDescriptor, is_callable: bool) -> String:\n\tassert(false, \"'get_template' must be implemented!\")\n\treturn \"\"\n\n\nfunc double(func_descriptor: GdFunctionDescriptor, is_callable: bool = false) -> PackedStringArray:\n\tvar is_static := func_descriptor.is_static()\n\tvar is_coroutine := func_descriptor.is_coroutine()\n\tvar func_name := func_descriptor.name()\n\tvar args := func_descriptor.args()\n\tvar varargs := func_descriptor.varargs()\n\tvar return_value := GdFunctionDoubler.default_return_value(func_descriptor)\n\tvar arg_names := extract_arg_names(args, true)\n\tvar vararg_names := extract_arg_names(varargs)\n\n\t# save original constructor arguments\n\tif func_name == \"_init\":\n\t\tvar constructor_args := \",\".join(GdFunctionDoubler.extract_constructor_args(args))\n\t\tvar constructor := \"func _init(%s) -> void:\\n\tsuper(%s)\\n\tpass\\n\" % [constructor_args, \", \".join(arg_names)]\n\t\treturn constructor.split(\"\\n\")\n\n\tvar double_src := \"@warning_ignore('shadowed_variable', 'untyped_declaration', 'unsafe_call_argument', 'unsafe_method_access')\\n\"\n\tif func_descriptor.is_engine():\n\t\tdouble_src += '@warning_ignore(\"native_method_override\")\\n'\n\tif func_descriptor.return_type() == GdObjects.TYPE_ENUM:\n\t\tdouble_src += '@warning_ignore(\"int_as_enum_without_match\")\\n'\n\t\tdouble_src += '@warning_ignore(\"int_as_enum_without_cast\")\\n'\n\tdouble_src += GdFunctionDoubler.extract_func_signature(func_descriptor)\n\t# fix to  unix format, this is need when the template is edited under windows than the template is stored with \\r\\n\n\tvar func_template := get_template(func_descriptor, is_callable).replace(\"\\r\\n\", \"\\n\")\n\tdouble_src += func_template\\\n\t\t.replace(\"$(arguments)\", \", \".join(arg_names))\\\n\t\t.replace(\"$(varargs)\", \", \".join(vararg_names))\\\n\t\t.replace(\"$(await)\", GdFunctionDoubler.await_is_coroutine(is_coroutine)) \\\n\t\t.replace(\"$(func_name)\", func_name )\\\n\t\t.replace(\"${default_return_value}\", return_value)\\\n\t\t.replace(\"$(push_errors)\", _push_errors)\n\n\tif is_static:\n\t\tdouble_src = double_src.replace(\"$(instance)\", \"__instance().\")\n\telse:\n\t\tdouble_src = double_src.replace(\"$(instance)\", \"\")\n\treturn double_src.split(\"\\n\")\n\n\nfunc extract_arg_names(argument_signatures: Array[GdFunctionArgument], add_suffix := false) -> PackedStringArray:\n\tvar arg_names := PackedStringArray()\n\tfor arg in argument_signatures:\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\targ_names.append(arg._name + (\"_\" if add_suffix else \"\"))\n\treturn arg_names\n\n\nstatic func extract_constructor_args(args :Array[GdFunctionArgument]) -> PackedStringArray:\n\tvar constructor_args := PackedStringArray()\n\tfor arg in args:\n\t\tvar arg_name := arg._name + \"_\"\n\t\tvar default_value := get_default(arg)\n\t\tif default_value == \"null\":\n\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\tconstructor_args.append(arg_name + \":Variant=\" + default_value)\n\t\telse:\n\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\tconstructor_args.append(arg_name + \":=\" + default_value)\n\treturn constructor_args\n\n\nstatic func extract_func_signature(descriptor: GdFunctionDescriptor) -> String:\n\tvar func_signature := \"\"\n\tif descriptor._return_type == TYPE_NIL:\n\t\tfunc_signature = \"func %s(%s) -> void:\" % [descriptor.name(), typeless_args(descriptor)]\n\telif descriptor._return_type == GdObjects.TYPE_VARIANT:\n\t\tfunc_signature = \"func %s(%s):\" % [descriptor.name(), typeless_args(descriptor)]\n\telse:\n\t\tfunc_signature = \"func %s(%s) -> %s:\" % [descriptor.name(), typeless_args(descriptor), descriptor.return_type_as_string()]\n\treturn \"static \" + func_signature if descriptor.is_static() else func_signature\n\n\nstatic func typeless_args(descriptor: GdFunctionDescriptor) -> String:\n\tvar collect := PackedStringArray()\n\tfor arg in descriptor.args():\n\t\tif arg.has_default():\n\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\tcollect.push_back(arg.name() + \"_\" + \"=\" + arg.value_as_string())\n\t\telse:\n\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\tcollect.push_back(arg.name() + \"_\")\n\tfor arg in descriptor.varargs():\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tcollect.push_back(arg.name() + \"=\" + arg.value_as_string())\n\treturn \", \".join(collect)\n\n\nstatic func get_default(arg :GdFunctionArgument) -> String:\n\tif arg.has_default():\n\t\treturn arg.value_as_string()\n\telse:\n\t\treturn DEFAULT_TYPED_RETURN_VALUES.get(arg.type(), \"null\")\n\n\nstatic func await_is_coroutine(is_coroutine :bool) -> String:\n\treturn \"await \" if is_coroutine else \"\"\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdFunctionDoubler.gd.uid",
    "content": "uid://dk4epec08vtj2\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdObjects.gd",
    "content": "# This is a helper class to compare two objects by equals\nclass_name GdObjects\nextends Resource\n\nconst GdUnitTools := preload(\"res://addons/gdUnit4/src/core/GdUnitTools.gd\")\n\n\n# introduced with Godot 4.3.beta1\nconst TYPE_PACKED_VECTOR4_ARRAY = 38 #TYPE_PACKED_VECTOR4_ARRAY\n\nconst TYPE_VOID \t= 1000\nconst TYPE_VARARG \t= 1001\nconst TYPE_VARIANT\t= 1002\nconst TYPE_FUNC \t= 1003\nconst TYPE_FUZZER \t= 1004\n# missing Godot types\nconst TYPE_NODE \t= 2001\nconst TYPE_CONTROL\t= 2002\nconst TYPE_CANVAS\t= 2003\nconst TYPE_ENUM\t\t= 2004\n\n\n# used as default value for varargs\nconst TYPE_VARARG_PLACEHOLDER_VALUE = \"__null__\"\n\n\nconst TYPE_AS_STRING_MAPPINGS := {\n\tTYPE_NIL: \"null\",\n\tTYPE_BOOL: \"bool\",\n\tTYPE_INT: \"int\",\n\tTYPE_FLOAT: \"float\",\n\tTYPE_STRING: \"String\",\n\tTYPE_VECTOR2: \"Vector2\",\n\tTYPE_VECTOR2I: \"Vector2i\",\n\tTYPE_RECT2: \"Rect2\",\n\tTYPE_RECT2I: \"Rect2i\",\n\tTYPE_VECTOR3: \"Vector3\",\n\tTYPE_VECTOR3I: \"Vector3i\",\n\tTYPE_TRANSFORM2D: \"Transform2D\",\n\tTYPE_VECTOR4: \"Vector4\",\n\tTYPE_VECTOR4I: \"Vector4i\",\n\tTYPE_PLANE: \"Plane\",\n\tTYPE_QUATERNION: \"Quaternion\",\n\tTYPE_AABB: \"AABB\",\n\tTYPE_BASIS: \"Basis\",\n\tTYPE_TRANSFORM3D: \"Transform3D\",\n\tTYPE_PROJECTION: \"Projection\",\n\tTYPE_COLOR: \"Color\",\n\tTYPE_STRING_NAME: \"StringName\",\n\tTYPE_NODE_PATH: \"NodePath\",\n\tTYPE_RID: \"RID\",\n\tTYPE_OBJECT: \"Object\",\n\tTYPE_CALLABLE: \"Callable\",\n\tTYPE_SIGNAL: \"Signal\",\n\tTYPE_DICTIONARY: \"Dictionary\",\n\tTYPE_ARRAY: \"Array\",\n\tTYPE_PACKED_BYTE_ARRAY: \"PackedByteArray\",\n\tTYPE_PACKED_INT32_ARRAY: \"PackedInt32Array\",\n\tTYPE_PACKED_INT64_ARRAY: \"PackedInt64Array\",\n\tTYPE_PACKED_FLOAT32_ARRAY: \"PackedFloat32Array\",\n\tTYPE_PACKED_FLOAT64_ARRAY: \"PackedFloat64Array\",\n\tTYPE_PACKED_STRING_ARRAY: \"PackedStringArray\",\n\tTYPE_PACKED_VECTOR2_ARRAY: \"PackedVector2Array\",\n\tTYPE_PACKED_VECTOR3_ARRAY: \"PackedVector3Array\",\n\tTYPE_PACKED_VECTOR4_ARRAY: \"PackedVector4Array\",\n\tTYPE_PACKED_COLOR_ARRAY: \"PackedColorArray\",\n\tTYPE_VOID: \"void\",\n\tTYPE_VARARG: \"VarArg\",\n\tTYPE_FUNC: \"Func\",\n\tTYPE_FUZZER: \"Fuzzer\",\n\tTYPE_VARIANT: \"Variant\"\n}\n\n\nconst NOTIFICATION_AS_STRING_MAPPINGS := {\n\tTYPE_OBJECT: {\n\t\tObject.NOTIFICATION_POSTINITIALIZE : \"POSTINITIALIZE\",\n\t\tObject.NOTIFICATION_PREDELETE: \"PREDELETE\",\n\t\tEditorSettings.NOTIFICATION_EDITOR_SETTINGS_CHANGED: \"EDITOR_SETTINGS_CHANGED\",\n\t},\n\tTYPE_NODE: {\n\t\tNode.NOTIFICATION_ENTER_TREE : \"ENTER_TREE\",\n\t\tNode.NOTIFICATION_EXIT_TREE: \"EXIT_TREE\",\n\t\tNode.NOTIFICATION_CHILD_ORDER_CHANGED: \"CHILD_ORDER_CHANGED\",\n\t\tNode.NOTIFICATION_READY: \"READY\",\n\t\tNode.NOTIFICATION_PAUSED: \"PAUSED\",\n\t\tNode.NOTIFICATION_UNPAUSED: \"UNPAUSED\",\n\t\tNode.NOTIFICATION_PHYSICS_PROCESS: \"PHYSICS_PROCESS\",\n\t\tNode.NOTIFICATION_PROCESS: \"PROCESS\",\n\t\tNode.NOTIFICATION_PARENTED: \"PARENTED\",\n\t\tNode.NOTIFICATION_UNPARENTED: \"UNPARENTED\",\n\t\tNode.NOTIFICATION_SCENE_INSTANTIATED: \"INSTANCED\",\n\t\tNode.NOTIFICATION_DRAG_BEGIN: \"DRAG_BEGIN\",\n\t\tNode.NOTIFICATION_DRAG_END: \"DRAG_END\",\n\t\tNode.NOTIFICATION_PATH_RENAMED: \"PATH_CHANGED\",\n\t\tNode.NOTIFICATION_INTERNAL_PROCESS: \"INTERNAL_PROCESS\",\n\t\tNode.NOTIFICATION_INTERNAL_PHYSICS_PROCESS: \"INTERNAL_PHYSICS_PROCESS\",\n\t\tNode.NOTIFICATION_POST_ENTER_TREE: \"POST_ENTER_TREE\",\n\t\tNode.NOTIFICATION_WM_MOUSE_ENTER: \"WM_MOUSE_ENTER\",\n\t\tNode.NOTIFICATION_WM_MOUSE_EXIT: \"WM_MOUSE_EXIT\",\n\t\tNode.NOTIFICATION_APPLICATION_FOCUS_IN: \"WM_FOCUS_IN\",\n\t\tNode.NOTIFICATION_APPLICATION_FOCUS_OUT: \"WM_FOCUS_OUT\",\n\t\t#Node.NOTIFICATION_WM_QUIT_REQUEST: \"WM_QUIT_REQUEST\",\n\t\tNode.NOTIFICATION_WM_GO_BACK_REQUEST: \"WM_GO_BACK_REQUEST\",\n\t\tNode.NOTIFICATION_WM_WINDOW_FOCUS_OUT: \"WM_UNFOCUS_REQUEST\",\n\t\tNode.NOTIFICATION_OS_MEMORY_WARNING: \"OS_MEMORY_WARNING\",\n\t\tNode.NOTIFICATION_TRANSLATION_CHANGED: \"TRANSLATION_CHANGED\",\n\t\tNode.NOTIFICATION_WM_ABOUT: \"WM_ABOUT\",\n\t\tNode.NOTIFICATION_CRASH: \"CRASH\",\n\t\tNode.NOTIFICATION_OS_IME_UPDATE: \"OS_IME_UPDATE\",\n\t\tNode.NOTIFICATION_APPLICATION_RESUMED: \"APP_RESUMED\",\n\t\tNode.NOTIFICATION_APPLICATION_PAUSED: \"APP_PAUSED\",\n\t\tNode3D.NOTIFICATION_TRANSFORM_CHANGED: \"TRANSFORM_CHANGED\",\n\t\tNode3D.NOTIFICATION_ENTER_WORLD: \"ENTER_WORLD\",\n\t\tNode3D.NOTIFICATION_EXIT_WORLD: \"EXIT_WORLD\",\n\t\tNode3D.NOTIFICATION_VISIBILITY_CHANGED: \"VISIBILITY_CHANGED\",\n\t\tSkeleton3D.NOTIFICATION_UPDATE_SKELETON: \"UPDATE_SKELETON\",\n\t\tCanvasItem.NOTIFICATION_DRAW: \"DRAW\",\n\t\tCanvasItem.NOTIFICATION_VISIBILITY_CHANGED: \"VISIBILITY_CHANGED\",\n\t\tCanvasItem.NOTIFICATION_ENTER_CANVAS: \"ENTER_CANVAS\",\n\t\tCanvasItem.NOTIFICATION_EXIT_CANVAS: \"EXIT_CANVAS\",\n\t\t#Popup.NOTIFICATION_POST_POPUP: \"POST_POPUP\",\n\t\t#Popup.NOTIFICATION_POPUP_HIDE: \"POPUP_HIDE\",\n\t},\n\tTYPE_CONTROL : {\n\t\tObject.NOTIFICATION_PREDELETE: \"PREDELETE\",\n\t\tContainer.NOTIFICATION_SORT_CHILDREN: \"SORT_CHILDREN\",\n\t\tControl.NOTIFICATION_RESIZED: \"RESIZED\",\n\t\tControl.NOTIFICATION_MOUSE_ENTER: \"MOUSE_ENTER\",\n\t\tControl.NOTIFICATION_MOUSE_EXIT: \"MOUSE_EXIT\",\n\t\tControl.NOTIFICATION_FOCUS_ENTER: \"FOCUS_ENTER\",\n\t\tControl.NOTIFICATION_FOCUS_EXIT: \"FOCUS_EXIT\",\n\t\tControl.NOTIFICATION_THEME_CHANGED: \"THEME_CHANGED\",\n\t\t#Control.NOTIFICATION_MODAL_CLOSE: \"MODAL_CLOSE\",\n\t\tControl.NOTIFICATION_SCROLL_BEGIN: \"SCROLL_BEGIN\",\n\t\tControl.NOTIFICATION_SCROLL_END: \"SCROLL_END\",\n\t}\n}\n\n\nenum COMPARE_MODE {\n\tOBJECT_REFERENCE,\n\tPARAMETER_DEEP_TEST\n}\n\n\n# prototype of better object to dictionary\n@warning_ignore(\"unsafe_cast\")\nstatic func obj2dict(obj: Object, hashed_objects := Dictionary()) -> Dictionary:\n\tif obj == null:\n\t\treturn {}\n\tvar clazz_name := obj.get_class()\n\tvar dict := Dictionary()\n\tvar clazz_path := \"\"\n\n\tif is_instance_valid(obj) and obj.get_script() != null:\n\t\tvar script: Script = obj.get_script()\n\t\t# handle build-in scripts\n\t\tif script.resource_path != null and script.resource_path.contains(\".tscn\"):\n\t\t\tvar path_elements := script.resource_path.split(\".tscn\")\n\t\t\tclazz_name = path_elements[0].get_file()\n\t\t\tclazz_path = script.resource_path\n\t\telse:\n\t\t\tvar d := inst_to_dict(obj)\n\t\t\tclazz_path = d[\"@path\"]\n\t\t\tif d[\"@subpath\"] != NodePath(\"\"):\n\t\t\t\tclazz_name = d[\"@subpath\"]\n\t\t\t\tdict[\"@inner_class\"] = true\n\t\t\telse:\n\t\t\t\tclazz_name = clazz_path.get_file().replace(\".gd\", \"\")\n\tdict[\"@path\"] = clazz_path\n\n\tfor property in obj.get_property_list():\n\t\tvar property_name :String = property[\"name\"]\n\t\tvar property_type :int = property[\"type\"]\n\t\tvar property_value :Variant = obj.get(property_name)\n\t\tif property_value is GDScript or property_value is Callable or property_value is RegEx:\n\t\t\tcontinue\n\t\tif (property[\"usage\"] & PROPERTY_USAGE_SCRIPT_VARIABLE|PROPERTY_USAGE_DEFAULT\n\t\t\tand not property[\"usage\"] & PROPERTY_USAGE_CATEGORY\n\t\t\tand not property[\"usage\"] == 0):\n\t\t\tif property_type == TYPE_OBJECT:\n\t\t\t\t# prevent recursion\n\t\t\t\tif hashed_objects.has(obj):\n\t\t\t\t\tdict[property_name] = str(property_value)\n\t\t\t\t\tcontinue\n\t\t\t\thashed_objects[obj] = true\n\t\t\t\tdict[property_name] = obj2dict(property_value as Object, hashed_objects)\n\t\t\telse:\n\t\t\t\tdict[property_name] = property_value\n\tif obj is Node:\n\t\tvar childrens :Array = (obj as Node).get_children()\n\t\tdict[\"childrens\"] = childrens.map(func (child :Object) -> Dictionary: return obj2dict(child, hashed_objects))\n\tif obj is TreeItem:\n\t\tvar childrens :Array = (obj as TreeItem).get_children()\n\t\tdict[\"childrens\"] = childrens.map(func (child :Object) -> Dictionary: return obj2dict(child, hashed_objects))\n\n\treturn {\"%s\" % clazz_name : dict}\n\n\nstatic func equals(obj_a :Variant, obj_b :Variant, case_sensitive :bool = false, compare_mode :COMPARE_MODE = COMPARE_MODE.PARAMETER_DEEP_TEST) -> bool:\n\treturn _equals(obj_a, obj_b, case_sensitive, compare_mode, [], 0)\n\n\nstatic func equals_sorted(obj_a: Array[Variant], obj_b: Array[Variant], case_sensitive: bool = false, compare_mode: COMPARE_MODE = COMPARE_MODE.PARAMETER_DEEP_TEST) -> bool:\n\tvar a: Array[Variant] = obj_a.duplicate()\n\tvar b: Array[Variant] = obj_b.duplicate()\n\ta.sort()\n\tb.sort()\n\treturn equals(a, b, case_sensitive, compare_mode)\n\n\n@warning_ignore(\"unsafe_method_access\", \"unsafe_cast\")\nstatic func _equals(obj_a :Variant, obj_b :Variant, case_sensitive :bool, compare_mode :COMPARE_MODE, deep_stack :Array, stack_depth :int ) -> bool:\n\tvar type_a := typeof(obj_a)\n\tvar type_b := typeof(obj_b)\n\tif stack_depth > 32:\n\t\tprints(\"stack_depth\", stack_depth, deep_stack)\n\t\tpush_error(\"GdUnit equals has max stack deep reached!\")\n\t\treturn false\n\n\t# use argument matcher if requested\n\tif is_instance_valid(obj_a) and obj_a is GdUnitArgumentMatcher:\n\t\treturn (obj_a as GdUnitArgumentMatcher).is_match(obj_b)\n\tif is_instance_valid(obj_b) and obj_b is GdUnitArgumentMatcher:\n\t\treturn (obj_b as GdUnitArgumentMatcher).is_match(obj_a)\n\n\tstack_depth += 1\n\t# fast fail is different types\n\tif not _is_type_equivalent(type_a, type_b):\n\t\treturn false\n\t# is same instance\n\tif obj_a == obj_b:\n\t\treturn true\n\t# handle null values\n\tif obj_a == null and obj_b != null:\n\t\treturn false\n\tif obj_b == null and obj_a != null:\n\t\treturn false\n\n\tmatch type_a:\n\t\tTYPE_OBJECT:\n\t\t\tif deep_stack.has(obj_a) or deep_stack.has(obj_b):\n\t\t\t\treturn true\n\t\t\tdeep_stack.append(obj_a)\n\t\t\tdeep_stack.append(obj_b)\n\t\t\tif compare_mode == COMPARE_MODE.PARAMETER_DEEP_TEST:\n\t\t\t\t# fail fast\n\t\t\t\tif not is_instance_valid(obj_a) or not is_instance_valid(obj_b):\n\t\t\t\t\treturn false\n\t\t\t\tif obj_a.get_class() != obj_b.get_class():\n\t\t\t\t\treturn false\n\t\t\t\tvar a := obj2dict(obj_a as Object)\n\t\t\t\tvar b := obj2dict(obj_b as Object)\n\t\t\t\treturn _equals(a, b, case_sensitive, compare_mode, deep_stack, stack_depth)\n\t\t\treturn obj_a == obj_b\n\n\t\tTYPE_ARRAY:\n\t\t\tif obj_a.size() != obj_b.size():\n\t\t\t\treturn false\n\t\t\tfor index :int in obj_a.size():\n\t\t\t\tif not _equals(obj_a[index], obj_b[index], case_sensitive, compare_mode, deep_stack, stack_depth):\n\t\t\t\t\treturn false\n\t\t\treturn true\n\n\t\tTYPE_DICTIONARY:\n\t\t\tif obj_a.size() != obj_b.size():\n\t\t\t\treturn false\n\t\t\tfor key :Variant in obj_a.keys():\n\t\t\t\tvar value_a :Variant = obj_a[key] if obj_a.has(key) else null\n\t\t\t\tvar value_b :Variant = obj_b[key] if obj_b.has(key) else null\n\t\t\t\tif not _equals(value_a, value_b, case_sensitive, compare_mode, deep_stack, stack_depth):\n\t\t\t\t\treturn false\n\t\t\treturn true\n\n\t\tTYPE_STRING:\n\t\t\tif case_sensitive:\n\t\t\t\treturn obj_a.to_lower() == obj_b.to_lower()\n\t\t\telse:\n\t\t\t\treturn obj_a == obj_b\n\treturn obj_a == obj_b\n\n\n@warning_ignore(\"shadowed_variable_base_class\")\nstatic func notification_as_string(instance :Variant, notification :int) -> String:\n\tvar error := \"Unknown notification: '%s' at instance:  %s\" % [notification, instance]\n\tif instance is Node and NOTIFICATION_AS_STRING_MAPPINGS[TYPE_NODE].has(notification):\n\t\treturn NOTIFICATION_AS_STRING_MAPPINGS[TYPE_NODE].get(notification, error)\n\tif instance is Control and NOTIFICATION_AS_STRING_MAPPINGS[TYPE_CONTROL].has(notification):\n\t\treturn NOTIFICATION_AS_STRING_MAPPINGS[TYPE_CONTROL].get(notification, error)\n\treturn NOTIFICATION_AS_STRING_MAPPINGS[TYPE_OBJECT].get(notification, error)\n\n\nstatic func string_to_type(value :String) -> int:\n\tfor type :int in TYPE_AS_STRING_MAPPINGS.keys():\n\t\tif TYPE_AS_STRING_MAPPINGS.get(type) == value:\n\t\t\treturn type\n\treturn TYPE_NIL\n\n\nstatic func to_camel_case(value :String) -> String:\n\tvar p := to_pascal_case(value)\n\tif not p.is_empty():\n\t\tp[0] = p[0].to_lower()\n\treturn p\n\n\nstatic func to_pascal_case(value :String) -> String:\n\treturn value.capitalize().replace(\" \", \"\")\n\n\n@warning_ignore(\"return_value_discarded\")\nstatic func to_snake_case(value :String) -> String:\n\tvar result := PackedStringArray()\n\tfor ch in value:\n\t\tvar lower_ch := ch.to_lower()\n\t\tif ch != lower_ch and result.size() > 1:\n\t\t\tresult.append('_')\n\t\tresult.append(lower_ch)\n\treturn ''.join(result)\n\n\nstatic func is_snake_case(value :String) -> bool:\n\tfor ch in value:\n\t\tif ch == '_':\n\t\t\tcontinue\n\t\tif ch == ch.to_upper():\n\t\t\treturn false\n\treturn true\n\n\nstatic func type_as_string(type :int) -> String:\n\tif type < TYPE_MAX:\n\t\treturn type_string(type)\n\treturn TYPE_AS_STRING_MAPPINGS.get(type, \"Variant\")\n\n\nstatic func typeof_as_string(value :Variant) -> String:\n\treturn TYPE_AS_STRING_MAPPINGS.get(typeof(value), \"Unknown type\")\n\n\nstatic func all_types() -> PackedInt32Array:\n\treturn PackedInt32Array(TYPE_AS_STRING_MAPPINGS.keys())\n\n\nstatic func string_as_typeof(type_name :String) -> int:\n\tvar type :Variant = TYPE_AS_STRING_MAPPINGS.find_key(type_name)\n\treturn type if type != null else TYPE_VARIANT\n\n\nstatic func is_primitive_type(value :Variant) -> bool:\n\treturn typeof(value) in [TYPE_BOOL, TYPE_STRING, TYPE_STRING_NAME, TYPE_INT, TYPE_FLOAT]\n\n\nstatic func _is_type_equivalent(type_a :int, type_b :int) -> bool:\n\t# don't test for TYPE_STRING_NAME equivalenz\n\tif type_a == TYPE_STRING_NAME or type_b == TYPE_STRING_NAME:\n\t\treturn true\n\tif GdUnitSettings.is_strict_number_type_compare():\n\t\treturn type_a == type_b\n\treturn (\n\t\t(type_a == TYPE_FLOAT and type_b == TYPE_INT)\n\t\tor (type_a == TYPE_INT and type_b == TYPE_FLOAT)\n\t\tor type_a == type_b)\n\n\nstatic func is_engine_type(value :Variant) -> bool:\n\tif value is GDScript or value is ScriptExtension:\n\t\treturn false\n\tvar obj: Object = value\n\tif is_instance_valid(obj) and obj.has_method(\"is_class\"):\n\t\treturn obj.is_class(\"GDScriptNativeClass\")\n\treturn false\n\n\nstatic func is_type(value :Variant) -> bool:\n\t# is an build-in type\n\tif typeof(value) != TYPE_OBJECT:\n\t\treturn false\n\t# is a engine class type\n\tif is_engine_type(value):\n\t\treturn true\n\t# is a custom class type\n\t@warning_ignore(\"unsafe_cast\")\n\tif value is GDScript and (value as GDScript).can_instantiate():\n\t\treturn true\n\treturn false\n\n\nstatic func _is_same(left :Variant, right :Variant) -> bool:\n\tvar left_type := -1 if left == null else typeof(left)\n\tvar right_type := -1 if right == null else typeof(right)\n\n\t# if typ different can't be the same\n\tif left_type != right_type:\n\t\treturn false\n\tif left_type == TYPE_OBJECT and right_type == TYPE_OBJECT:\n\t\t@warning_ignore(\"unsafe_cast\")\n\t\treturn (left as Object).get_instance_id() == (right as Object).get_instance_id()\n\treturn equals(left, right)\n\n\nstatic func is_object(value :Variant) -> bool:\n\treturn typeof(value) == TYPE_OBJECT\n\n\nstatic func is_script(value :Variant) -> bool:\n\treturn is_object(value) and value is Script\n\n\nstatic func is_test_suite(script :Script) -> bool:\n\treturn is_gd_testsuite(script) or GdUnit4CSharpApiLoader.is_test_suite(script.resource_path)\n\n\nstatic func is_native_class(value :Variant) -> bool:\n\treturn is_object(value) and is_engine_type(value)\n\n\nstatic func is_scene(value :Variant) -> bool:\n\treturn is_object(value) and value is PackedScene\n\n\nstatic func is_scene_resource_path(value :Variant) -> bool:\n\t@warning_ignore(\"unsafe_cast\")\n\treturn value is String and (value as String).ends_with(\".tscn\")\n\n\nstatic func is_gd_script(script :Script) -> bool:\n\treturn script is GDScript\n\n\nstatic func is_cs_script(script :Script) -> bool:\n\t# we need to check by stringify name because checked non mono Godot the class CSharpScript is not available\n\treturn str(script).find(\"CSharpScript\") != -1\n\n\nstatic func is_gd_testsuite(script :Script) -> bool:\n\tif is_gd_script(script):\n\t\tvar stack := [script]\n\t\twhile not stack.is_empty():\n\t\t\tvar current: Script = stack.pop_front()\n\t\t\tvar base: Script = current.get_base_script()\n\t\t\tif base != null:\n\t\t\t\tif base.resource_path.find(\"GdUnitTestSuite\") != -1:\n\t\t\t\t\treturn true\n\t\t\t\tstack.push_back(base)\n\treturn false\n\n\nstatic func is_singleton(value: Variant) -> bool:\n\tif not is_instance_valid(value) or is_native_class(value):\n\t\treturn false\n\tfor name in Engine.get_singleton_list():\n\t\t@warning_ignore(\"unsafe_cast\")\n\t\tif (value as Object).is_class(name):\n\t\t\treturn true\n\treturn false\n\n\nstatic func is_instance(value :Variant) -> bool:\n\tif not is_instance_valid(value) or is_native_class(value):\n\t\treturn false\n\t@warning_ignore(\"unsafe_cast\")\n\tif is_script(value) and (value as Script).get_instance_base_type() == \"\":\n\t\treturn true\n\tif is_scene(value):\n\t\treturn true\n\t@warning_ignore(\"unsafe_cast\")\n\treturn not (value as Object).has_method('new') and not (value as Object).has_method('instance')\n\n\n# only object form type Node and attached filename\nstatic func is_instance_scene(instance :Variant) -> bool:\n\tif instance is Node:\n\t\tvar node: Node = instance\n\t\treturn node.get_scene_file_path() != null and not node.get_scene_file_path().is_empty()\n\treturn false\n\n\nstatic func can_be_instantiate(obj :Variant) -> bool:\n\tif not obj or is_engine_type(obj):\n\t\treturn false\n\t@warning_ignore(\"unsafe_cast\")\n\treturn (obj as Object).has_method(\"new\")\n\n\nstatic func create_instance(clazz :Variant) -> GdUnitResult:\n\tmatch typeof(clazz):\n\t\tTYPE_OBJECT:\n\t\t\t# test is given clazz already an instance\n\t\t\tif is_instance(clazz):\n\t\t\t\treturn GdUnitResult.success(clazz)\n\t\t\t@warning_ignore(\"unsafe_method_access\")\n\t\t\treturn GdUnitResult.success(clazz.new())\n\t\tTYPE_STRING:\n\t\t\tvar clazz_name: String = clazz\n\t\t\tif ClassDB.class_exists(clazz_name):\n\t\t\t\tif Engine.has_singleton(clazz_name):\n\t\t\t\t\treturn GdUnitResult.error(\"Not allowed to create a instance for singelton '%s'.\" % clazz_name)\n\t\t\t\tif not ClassDB.can_instantiate(clazz_name):\n\t\t\t\t\treturn  GdUnitResult.error(\"Can't instance Engine class '%s'.\" % clazz_name)\n\t\t\t\treturn GdUnitResult.success(ClassDB.instantiate(clazz_name))\n\t\t\telse:\n\t\t\t\tvar clazz_path :String = extract_class_path(clazz_name)[0]\n\t\t\t\tif not FileAccess.file_exists(clazz_path):\n\t\t\t\t\treturn GdUnitResult.error(\"Class '%s' not found.\" % clazz_name)\n\t\t\t\tvar script: GDScript = load(clazz_path)\n\t\t\t\tif script != null:\n\t\t\t\t\treturn GdUnitResult.success(script.new())\n\t\t\t\telse:\n\t\t\t\t\treturn GdUnitResult.error(\"Can't create instance for '%s'.\" % clazz_name)\n\treturn GdUnitResult.error(\"Can't create instance for class '%s'.\" % str(clazz))\n\n\n@warning_ignore(\"return_value_discarded\")\nstatic func extract_class_path(clazz :Variant) -> PackedStringArray:\n\tvar clazz_path := PackedStringArray()\n\tif clazz is String:\n\t\t@warning_ignore(\"unsafe_cast\")\n\t\tclazz_path.append(clazz as String)\n\t\treturn clazz_path\n\tif is_instance(clazz):\n\t\t# is instance a script instance?\n\t\tvar script: GDScript = clazz.script\n\t\tif script != null:\n\t\t\treturn extract_class_path(script)\n\t\treturn clazz_path\n\n\tif clazz is GDScript:\n\t\tvar script: GDScript = clazz\n\t\tif not script.resource_path.is_empty():\n\t\t\tclazz_path.append(script.resource_path)\n\t\t\treturn clazz_path\n\t\t# if not found we go the expensive way and extract the path form the script by creating an instance\n\t\tvar arg_list := build_function_default_arguments(script, \"_init\")\n\t\tvar instance: Object = script.callv(\"new\", arg_list)\n\t\tvar clazz_info := inst_to_dict(instance)\n\t\tGdUnitTools.free_instance(instance)\n\t\t@warning_ignore(\"unsafe_cast\")\n\t\tclazz_path.append(clazz_info[\"@path\"] as String)\n\t\tif clazz_info.has(\"@subpath\"):\n\t\t\tvar sub_path :String = clazz_info[\"@subpath\"]\n\t\t\tif not sub_path.is_empty():\n\t\t\t\tvar sub_paths := sub_path.split(\"/\")\n\t\t\t\tclazz_path += sub_paths\n\t\treturn clazz_path\n\treturn clazz_path\n\n\nstatic func extract_class_name_from_class_path(clazz_path :PackedStringArray) -> String:\n\tvar base_clazz := clazz_path[0]\n\t# return original class name if engine class\n\tif ClassDB.class_exists(base_clazz):\n\t\treturn base_clazz\n\tvar clazz_name := to_pascal_case(base_clazz.get_basename().get_file())\n\tfor path_index in range(1, clazz_path.size()):\n\t\tclazz_name += \".\" + clazz_path[path_index]\n\treturn  clazz_name\n\n\nstatic func extract_class_name(clazz :Variant) -> GdUnitResult:\n\tif clazz == null:\n\t\treturn GdUnitResult.error(\"Can't extract class name form a null value.\")\n\n\tif is_instance(clazz):\n\t\t# is instance a script instance?\n\t\tvar script: GDScript = clazz.script\n\t\tif script != null:\n\t\t\treturn extract_class_name(script)\n\t\t@warning_ignore(\"unsafe_cast\")\n\t\treturn GdUnitResult.success((clazz as Object).get_class())\n\n\t# extract name form full qualified class path\n\tif clazz is String:\n\t\tvar clazz_name: String = clazz\n\t\tif ClassDB.class_exists(clazz_name):\n\t\t\treturn GdUnitResult.success(clazz_name)\n\t\tvar source_script :GDScript = load(clazz_name)\n\t\tclazz_name = GdScriptParser.new().get_class_name(source_script)\n\t\treturn GdUnitResult.success(to_pascal_case(clazz_name))\n\n\tif is_primitive_type(clazz):\n\t\treturn GdUnitResult.error(\"Can't extract class name for an primitive '%s'\" % type_as_string(typeof(clazz)))\n\n\tif is_script(clazz):\n\t\t@warning_ignore(\"unsafe_cast\")\n\t\tif (clazz as Script).resource_path.is_empty():\n\t\t\tvar class_path := extract_class_name_from_class_path(extract_class_path(clazz))\n\t\t\treturn GdUnitResult.success(class_path);\n\t\treturn extract_class_name(clazz.resource_path)\n\n\t# need to create an instance for a class typ the extract the class name\n\t@warning_ignore(\"unsafe_method_access\")\n\tvar instance :Variant = clazz.new()\n\tif instance == null:\n\t\treturn GdUnitResult.error(\"Can't create a instance for class '%s'\" % str(clazz))\n\tvar result := extract_class_name(instance)\n\t@warning_ignore(\"return_value_discarded\")\n\tGdUnitTools.free_instance(instance)\n\treturn result\n\n\nstatic func extract_inner_clazz_names(clazz_name :String, script_path :PackedStringArray) -> PackedStringArray:\n\tvar inner_classes := PackedStringArray()\n\n\tif ClassDB.class_exists(clazz_name):\n\t\treturn inner_classes\n\tvar script :GDScript = load(script_path[0])\n\tvar map := script.get_script_constant_map()\n\tfor key :String in map.keys():\n\t\tvar value :Variant = map.get(key)\n\t\tif value is GDScript:\n\t\t\tvar class_path := extract_class_path(value)\n\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\tinner_classes.append(class_path[1])\n\treturn inner_classes\n\n\nstatic func extract_class_functions(clazz_name :String, script_path :PackedStringArray) -> Array:\n\tif ClassDB.class_get_method_list(clazz_name):\n\t\treturn ClassDB.class_get_method_list(clazz_name)\n\n\tif not FileAccess.file_exists(script_path[0]):\n\t\treturn Array()\n\tvar script :GDScript = load(script_path[0])\n\tif script is GDScript:\n\t\t# if inner class on class path we have to load the script from the script_constant_map\n\t\tif script_path.size() == 2 and script_path[1] != \"\":\n\t\t\tvar inner_classes := script_path[1]\n\t\t\tvar map := script.get_script_constant_map()\n\t\t\tscript = map[inner_classes]\n\t\tvar clazz_functions :Array = script.get_method_list()\n\t\tvar base_clazz :String = script.get_instance_base_type()\n\t\tif base_clazz:\n\t\t\treturn extract_class_functions(base_clazz, script_path)\n\t\treturn clazz_functions\n\treturn Array()\n\n\n# scans all registert script classes for given <clazz_name>\n# if the class is public in the global space than return true otherwise false\n# public class means the script class is defined by 'class_name <name>'\nstatic func is_public_script_class(clazz_name :String) -> bool:\n\tvar script_classes:Array[Dictionary] = ProjectSettings.get_global_class_list()\n\tfor class_info in script_classes:\n\t\tif class_info.has(\"class\"):\n\t\t\tif class_info[\"class\"] == clazz_name:\n\t\t\t\treturn true\n\treturn false\n\n\nstatic func build_function_default_arguments(script :GDScript, func_name :String) -> Array:\n\tvar arg_list := Array()\n\tfor func_sig in script.get_script_method_list():\n\t\tif func_sig[\"name\"] == func_name:\n\t\t\tvar args :Array[Dictionary] = func_sig[\"args\"]\n\t\t\tfor arg in args:\n\t\t\t\tvar value_type :int = arg[\"type\"]\n\t\t\t\tvar default_value :Variant = default_value_by_type(value_type)\n\t\t\t\targ_list.append(default_value)\n\t\t\treturn arg_list\n\treturn arg_list\n\n\nstatic func default_value_by_type(type :int) -> Variant:\n\tassert(type < TYPE_MAX)\n\tassert(type >= 0)\n\n\tmatch type:\n\t\tTYPE_NIL: return null\n\t\tTYPE_BOOL: return false\n\t\tTYPE_INT: return 0\n\t\tTYPE_FLOAT: return 0.0\n\t\tTYPE_STRING: return \"\"\n\t\tTYPE_VECTOR2: return Vector2.ZERO\n\t\tTYPE_VECTOR2I: return Vector2i.ZERO\n\t\tTYPE_VECTOR3: return Vector3.ZERO\n\t\tTYPE_VECTOR3I: return Vector3i.ZERO\n\t\tTYPE_VECTOR4: return Vector4.ZERO\n\t\tTYPE_VECTOR4I: return Vector4i.ZERO\n\t\tTYPE_RECT2: return Rect2()\n\t\tTYPE_RECT2I: return Rect2i()\n\t\tTYPE_TRANSFORM2D: return Transform2D()\n\t\tTYPE_PLANE: return Plane()\n\t\tTYPE_QUATERNION: return Quaternion()\n\t\tTYPE_AABB: return AABB()\n\t\tTYPE_BASIS: return Basis()\n\t\tTYPE_TRANSFORM3D: return Transform3D()\n\t\tTYPE_COLOR: return Color()\n\t\tTYPE_NODE_PATH: return NodePath()\n\t\tTYPE_RID: return RID()\n\t\tTYPE_OBJECT: return null\n\t\tTYPE_CALLABLE: return Callable()\n\t\tTYPE_ARRAY: return []\n\t\tTYPE_DICTIONARY: return {}\n\t\tTYPE_PACKED_BYTE_ARRAY: return PackedByteArray()\n\t\tTYPE_PACKED_COLOR_ARRAY: return PackedColorArray()\n\t\tTYPE_PACKED_INT32_ARRAY: return PackedInt32Array()\n\t\tTYPE_PACKED_INT64_ARRAY: return PackedInt64Array()\n\t\tTYPE_PACKED_FLOAT32_ARRAY: return PackedFloat32Array()\n\t\tTYPE_PACKED_FLOAT64_ARRAY: return PackedFloat64Array()\n\t\tTYPE_PACKED_STRING_ARRAY: return PackedStringArray()\n\t\tTYPE_PACKED_VECTOR2_ARRAY: return PackedVector2Array()\n\t\tTYPE_PACKED_VECTOR3_ARRAY: return PackedVector3Array()\n\n\tpush_error(\"Can't determine a default value for type: '%s', Please create a Bug issue and attach the stacktrace please.\" % type)\n\treturn null\n\n\nstatic func find_nodes_by_class(root: Node, cls: String, recursive: bool = false) -> Array[Node]:\n\tif not recursive:\n\t\treturn _find_nodes_by_class_no_rec(root, cls)\n\treturn _find_nodes_by_class(root, cls)\n\n\nstatic func _find_nodes_by_class_no_rec(parent: Node, cls: String) -> Array[Node]:\n\tvar result :Array[Node] = []\n\tfor ch in parent.get_children():\n\t\tif ch.get_class() == cls:\n\t\t\tresult.append(ch)\n\treturn result\n\n\nstatic func _find_nodes_by_class(root: Node, cls: String) -> Array[Node]:\n\tvar result :Array[Node] = []\n\tvar stack  :Array[Node] = [root]\n\twhile stack:\n\t\tvar node :Node = stack.pop_back()\n\t\tif node.get_class() == cls:\n\t\t\tresult.append(node)\n\t\tfor ch in node.get_children():\n\t\t\tstack.push_back(ch)\n\treturn result\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdObjects.gd.uid",
    "content": "uid://dc02g2g1th4oy\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnit4Version.gd",
    "content": "class_name GdUnit4Version\nextends RefCounted\n\nconst VERSION_PATTERN = \"[center][color=#9887c4]gd[/color][color=#7a57d6]Unit[/color][color=#9887c4]4[/color] [color=#9887c4]${version}[/color][/center]\"\n\nvar _major :int\nvar _minor :int\nvar _patch :int\n\n\nfunc _init(major :int, minor :int, patch :int) -> void:\n\t_major = major\n\t_minor = minor\n\t_patch = patch\n\n\nstatic func parse(value :String) -> GdUnit4Version:\n\tvar regex := RegEx.new()\n\t@warning_ignore(\"return_value_discarded\")\n\tregex.compile(\"[a-zA-Z:,-]+\")\n\tvar cleaned := regex.sub(value, \"\", true)\n\tvar parts := cleaned.split(\".\")\n\tvar major := parts[0].to_int()\n\tvar minor := parts[1].to_int()\n\tvar patch := parts[2].to_int() if parts.size() > 2 else 0\n\treturn GdUnit4Version.new(major, minor, patch)\n\n\nstatic func current() -> GdUnit4Version:\n\tvar config := ConfigFile.new()\n\t@warning_ignore(\"return_value_discarded\")\n\tconfig.load('addons/gdUnit4/plugin.cfg')\n\t@warning_ignore(\"unsafe_cast\")\n\treturn parse(config.get_value('plugin', 'version') as String)\n\n\nfunc equals(other :GdUnit4Version) -> bool:\n\treturn _major == other._major and _minor == other._minor and _patch == other._patch\n\n\nfunc is_greater(other :GdUnit4Version) -> bool:\n\tif _major > other._major:\n\t\treturn true\n\tif _major == other._major and _minor > other._minor:\n\t\treturn true\n\treturn _major == other._major and _minor == other._minor and _patch > other._patch\n\n\nstatic func init_version_label(label :Control) -> void:\n\tvar config := ConfigFile.new()\n\t@warning_ignore(\"return_value_discarded\")\n\tconfig.load('addons/gdUnit4/plugin.cfg')\n\tvar version :String = config.get_value('plugin', 'version')\n\tif label is RichTextLabel:\n\t\t(label as RichTextLabel).text = VERSION_PATTERN.replace('${version}', version)\n\telse:\n\t\t(label as Label).text = \"gdUnit4 \" + version\n\n\nfunc _to_string() -> String:\n\treturn \"v%d.%d.%d\" % [_major, _minor, _patch]\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnit4Version.gd.uid",
    "content": "uid://cd1awsisgh3uq\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitClassDoubler.gd",
    "content": "# A class doubler used to mock and spy checked implementations\nclass_name GdUnitClassDoubler\nextends RefCounted\n\n\nconst DOUBLER_INSTANCE_ID_PREFIX := \"gdunit_doubler_instance_id_\"\nconst DOUBLER_TEMPLATE :GDScript = preload(\"res://addons/gdUnit4/src/core/GdUnitObjectInteractionsTemplate.gd\")\nconst EXCLUDE_VIRTUAL_FUNCTIONS = [\n\t# we have to exclude notifications because NOTIFICATION_PREDELETE is try\n\t# to delete already freed spy/mock resources and will result in a conflict\n\t\"_notification\",\n\t# https://github.com/godotengine/godot/issues/67461\n\t\"get_name\",\n\t\"get_path\",\n\t\"duplicate\",\n\t]\n# define functions to be exclude when spy or mock checked a scene\nconst EXLCUDE_SCENE_FUNCTIONS = [\n\t# needs to exclude get/set script functions otherwise it endsup in recursive endless loop\n\t\"set_script\",\n\t\"get_script\",\n\t# needs to exclude otherwise verify fails checked collection arguments checked calling to string\n\t\"_to_string\",\n]\nconst EXCLUDE_FUNCTIONS = [\"new\", \"free\", \"get_instance_id\", \"get_tree\"]\n\n\nstatic func check_leaked_instances() -> void:\n\t## we check that all registered spy/mock instances are removed from the engine meta data\n\tfor key in Engine.get_meta_list():\n\t\tif key.begins_with(DOUBLER_INSTANCE_ID_PREFIX):\n\t\t\tvar instance :Variant = Engine.get_meta(key)\n\t\t\tpush_error(\"GdUnit internal error: an spy/mock instance '%s', class:'%s' is not removed from the engine and will lead in a leaked instance!\" % [instance, instance.__SOURCE_CLASS])\n\n\n# loads the doubler template\n# class_info = { \"class_name\": <>, \"class_path\" : <>}\nstatic func load_template(template :String, class_info :Dictionary, instance :Object) -> PackedStringArray:\n\t# store instance id\n\tvar clazz_name: String = class_info.get(\"class_name\")\n\tvar source_code := template\\\n\t\t.replace(\"${instance_id}\", \"%s%d\" % [DOUBLER_INSTANCE_ID_PREFIX, abs(instance.get_instance_id())])\\\n\t\t.replace(\"${source_class}\", clazz_name)\n\tvar lines := GdScriptParser.to_unix_format(source_code).split(\"\\n\")\n\t# replace template class_name with Doubled<class> name and extends form source class\n\t@warning_ignore(\"return_value_discarded\")\n\tlines.insert(0, \"class_name Doubled%s\" % clazz_name.replace(\".\", \"_\"))\n\t@warning_ignore(\"return_value_discarded\")\n\tlines.insert(1, extends_clazz(class_info))\n\t# append Object interactions stuff\n\tlines.append_array(GdScriptParser.to_unix_format(DOUBLER_TEMPLATE.source_code).split(\"\\n\"))\n\treturn lines\n\n\nstatic func extends_clazz(class_info :Dictionary) -> String:\n\tvar clazz_name :String = class_info.get(\"class_name\")\n\tvar clazz_path :PackedStringArray = class_info.get(\"class_path\", [])\n\t# is inner class?\n\tif clazz_path.size() > 1:\n\t\treturn \"extends %s\" % clazz_name\n\tif clazz_path.size() == 1 and clazz_path[0].ends_with(\".gd\"):\n\t\treturn \"extends '%s'\" % clazz_path[0]\n\treturn \"extends %s\" % clazz_name\n\n\n# double all functions of given instance\nstatic func double_functions(instance :Object, clazz_name :String, clazz_path :PackedStringArray, func_doubler: GdFunctionDoubler, exclude_functions :Array) -> PackedStringArray:\n\tvar doubled_source := PackedStringArray()\n\tvar parser := GdScriptParser.new()\n\tvar exclude_override_functions := EXCLUDE_VIRTUAL_FUNCTIONS + EXCLUDE_FUNCTIONS + exclude_functions\n\tvar functions := Array()\n\n\t# double script functions\n\tif not ClassDB.class_exists(clazz_name):\n\t\tvar result := parser.parse(clazz_name, clazz_path)\n\t\tif result.is_error():\n\t\t\tpush_error(result.error_message())\n\t\t\treturn PackedStringArray()\n\t\tvar class_descriptor :GdClassDescriptor = result.value()\n\t\tfor func_descriptor in class_descriptor.functions():\n\t\t\tif instance != null and not instance.has_method(func_descriptor.name()):\n\t\t\t\t#prints(\"no virtual func implemented\",clazz_name, func_descriptor.name() )\n\t\t\t\tcontinue\n\t\t\tif functions.has(func_descriptor.name()) or exclude_override_functions.has(func_descriptor.name()):\n\t\t\t\tcontinue\n\t\t\tdoubled_source += func_doubler.double(func_descriptor, instance is CallableDoubler)\n\t\t\tfunctions.append(func_descriptor.name())\n\n\t# double regular class functions\n\tvar clazz_functions := GdObjects.extract_class_functions(clazz_name, clazz_path)\n\tfor method : Dictionary in clazz_functions:\n\t\tvar func_descriptor := GdFunctionDescriptor.extract_from(method)\n\t\t# exclude private core functions\n\t\tif func_descriptor.is_private():\n\t\t\tcontinue\n\t\tif functions.has(func_descriptor.name()) or exclude_override_functions.has(func_descriptor.name()):\n\t\t\tcontinue\n\t\t# GD-110: Hotfix do not double invalid engine functions\n\t\tif is_invalid_method_descriptior(method):\n\t\t\t#prints(\"'%s': invalid method descriptor found! %s\" % [clazz_name, method])\n\t\t\tcontinue\n\t\t# do not double on not implemented virtual functions\n\t\tif instance != null and not instance.has_method(func_descriptor.name()):\n\t\t\t#prints(\"no virtual func implemented\",clazz_name, func_descriptor.name() )\n\t\t\tcontinue\n\t\tfunctions.append(func_descriptor.name())\n\t\tdoubled_source.append_array(func_doubler.double(func_descriptor, instance is CallableDoubler))\n\treturn doubled_source\n\n\n# GD-110\nstatic func is_invalid_method_descriptior(method :Dictionary) -> bool:\n\tvar return_info :Dictionary = method[\"return\"]\n\tvar type :int = return_info[\"type\"]\n\tvar usage :int = return_info[\"usage\"]\n\tvar clazz_name :String = return_info[\"class_name\"]\n\t# is method returning a type int with a given 'class_name' we have an enum\n\t# and the PROPERTY_USAGE_CLASS_IS_ENUM must be set\n\tif type == TYPE_INT and not clazz_name.is_empty() and not (usage & PROPERTY_USAGE_CLASS_IS_ENUM):\n\t\treturn true\n\tif clazz_name == \"Variant.Type\":\n\t\treturn true\n\treturn false\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitClassDoubler.gd.uid",
    "content": "uid://b611k0ri8qpm6\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitFileAccess.gd",
    "content": "class_name GdUnitFileAccess\nextends RefCounted\n\nconst GDUNIT_TEMP := \"user://tmp\"\n\n\nstatic func current_dir() -> String:\n\treturn ProjectSettings.globalize_path(\"res://\")\n\n\nstatic func clear_tmp() -> void:\n\tdelete_directory(GDUNIT_TEMP)\n\n\n# Creates a new file under\nstatic func create_temp_file(relative_path :String, file_name :String, mode := FileAccess.WRITE) -> FileAccess:\n\tvar file_path := create_temp_dir(relative_path) + \"/\" + file_name\n\tvar file := FileAccess.open(file_path, mode)\n\tif file == null:\n\t\tpush_error(\"Error creating temporary file at: %s, %s\" % [file_path, error_string(FileAccess.get_open_error())])\n\treturn file\n\n\nstatic func temp_dir() -> String:\n\tif not DirAccess.dir_exists_absolute(GDUNIT_TEMP):\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tDirAccess.make_dir_recursive_absolute(GDUNIT_TEMP)\n\treturn GDUNIT_TEMP\n\n\nstatic func create_temp_dir(folder_name :String) -> String:\n\tvar new_folder := temp_dir() + \"/\" + folder_name\n\tif not DirAccess.dir_exists_absolute(new_folder):\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tDirAccess.make_dir_recursive_absolute(new_folder)\n\treturn new_folder\n\n\nstatic func copy_file(from_file :String, to_dir :String) -> GdUnitResult:\n\tvar dir := DirAccess.open(to_dir)\n\tif dir != null:\n\t\tvar to_file := to_dir + \"/\" + from_file.get_file()\n\t\tprints(\"Copy %s to %s\" % [from_file, to_file])\n\t\tvar error := dir.copy(from_file, to_file)\n\t\tif error != OK:\n\t\t\treturn GdUnitResult.error(\"Can't copy file form '%s' to '%s'. Error: '%s'\" % [from_file, to_file, error_string(error)])\n\t\treturn GdUnitResult.success(to_file)\n\treturn GdUnitResult.error(\"Directory not found: \" + to_dir)\n\n\nstatic func copy_directory(from_dir :String, to_dir :String, recursive :bool = false) -> bool:\n\tif not DirAccess.dir_exists_absolute(from_dir):\n\t\tpush_error(\"Source directory not found '%s'\" % from_dir)\n\t\treturn false\n\n\t# check if destination exists\n\tif not DirAccess.dir_exists_absolute(to_dir):\n\t\t# create it\n\t\tvar err := DirAccess.make_dir_recursive_absolute(to_dir)\n\t\tif err != OK:\n\t\t\tpush_error(\"Can't create directory '%s'. Error: %s\" % [to_dir, error_string(err)])\n\t\t\treturn false\n\tvar source_dir := DirAccess.open(from_dir)\n\tvar dest_dir := DirAccess.open(to_dir)\n\tif source_dir != null:\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tsource_dir.list_dir_begin()\n\t\tvar next := \".\"\n\n\t\twhile next != \"\":\n\t\t\tnext = source_dir.get_next()\n\t\t\tif next == \"\" or next == \".\" or next == \"..\":\n\t\t\t\tcontinue\n\t\t\tvar source := source_dir.get_current_dir() + \"/\" + next\n\t\t\tvar dest := dest_dir.get_current_dir() + \"/\" + next\n\t\t\tif source_dir.current_is_dir():\n\t\t\t\tif recursive:\n\t\t\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t\t\tcopy_directory(source + \"/\", dest, recursive)\n\t\t\t\tcontinue\n\t\t\tvar err := source_dir.copy(source, dest)\n\t\t\tif err != OK:\n\t\t\t\tpush_error(\"Error checked copy file '%s' to '%s'\" % [source, dest])\n\t\t\t\treturn false\n\n\t\treturn true\n\telse:\n\t\tpush_error(\"Directory not found: \" + from_dir)\n\t\treturn false\n\n\nstatic func delete_directory(path :String, only_content := false) -> void:\n\tvar dir := DirAccess.open(path)\n\tif dir != null:\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tdir.list_dir_begin()\n\t\tvar file_name := \".\"\n\t\twhile file_name != \"\":\n\t\t\tfile_name = dir.get_next()\n\t\t\tif file_name.is_empty() or file_name == \".\" or file_name == \"..\":\n\t\t\t\tcontinue\n\t\t\tvar next := path + \"/\" +file_name\n\t\t\tif dir.current_is_dir():\n\t\t\t\tdelete_directory(next)\n\t\t\telse:\n\t\t\t\t# delete file\n\t\t\t\tvar err := dir.remove(next)\n\t\t\t\tif err:\n\t\t\t\t\tpush_error(\"Delete %s failed: %s\" % [next, error_string(err)])\n\t\tif not only_content:\n\t\t\tvar err := dir.remove(path)\n\t\t\tif err:\n\t\t\t\tpush_error(\"Delete %s failed: %s\" % [path, error_string(err)])\n\n\nstatic func delete_path_index_lower_equals_than(path :String, prefix :String, index :int) -> int:\n\tvar dir := DirAccess.open(path)\n\tif dir == null:\n\t\treturn 0\n\tvar deleted := 0\n\t@warning_ignore(\"return_value_discarded\")\n\tdir.list_dir_begin()\n\tvar next := \".\"\n\twhile next != \"\":\n\t\tnext = dir.get_next()\n\t\tif next.is_empty() or next == \".\" or next == \"..\":\n\t\t\tcontinue\n\t\tif next.begins_with(prefix):\n\t\t\tvar current_index := next.split(\"_\")[1].to_int()\n\t\t\tif current_index <= index:\n\t\t\t\tdeleted += 1\n\t\t\t\tdelete_directory(path + \"/\" + next)\n\treturn deleted\n\n\n# scans given path for sub directories by given prefix and returns the highest index numer\n# e.g. <prefix_%d>\nstatic func find_last_path_index(path :String, prefix :String) -> int:\n\tvar dir := DirAccess.open(path)\n\tif dir == null:\n\t\treturn 0\n\tvar last_iteration := 0\n\t@warning_ignore(\"return_value_discarded\")\n\tdir.list_dir_begin()\n\tvar next := \".\"\n\twhile next != \"\":\n\t\tnext = dir.get_next()\n\t\tif next.is_empty() or next == \".\" or next == \"..\":\n\t\t\tcontinue\n\t\tif next.begins_with(prefix):\n\t\t\tvar iteration := next.split(\"_\")[1].to_int()\n\t\t\tif iteration > last_iteration:\n\t\t\t\tlast_iteration = iteration\n\treturn last_iteration\n\n\nstatic func scan_dir(path :String) -> PackedStringArray:\n\tvar dir := DirAccess.open(path)\n\tif dir == null or not dir.dir_exists(path):\n\t\treturn PackedStringArray()\n\tvar content := PackedStringArray()\n\t@warning_ignore(\"return_value_discarded\")\n\tdir.list_dir_begin()\n\tvar next := \".\"\n\twhile next != \"\":\n\t\tnext = dir.get_next()\n\t\tif next.is_empty() or next == \".\" or next == \"..\":\n\t\t\tcontinue\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tcontent.append(next)\n\treturn content\n\n\nstatic func resource_as_array(resource_path :String) -> PackedStringArray:\n\tvar file := FileAccess.open(resource_path, FileAccess.READ)\n\tif file == null:\n\t\tpush_error(\"ERROR: Can't read resource '%s'. %s\" % [resource_path, error_string(FileAccess.get_open_error())])\n\t\treturn PackedStringArray()\n\tvar file_content := PackedStringArray()\n\twhile not file.eof_reached():\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tfile_content.append(file.get_line())\n\treturn file_content\n\n\nstatic func resource_as_string(resource_path :String) -> String:\n\tvar file := FileAccess.open(resource_path, FileAccess.READ)\n\tif file == null:\n\t\tpush_error(\"ERROR: Can't read resource '%s'. %s\" % [resource_path, error_string(FileAccess.get_open_error())])\n\t\treturn \"\"\n\treturn file.get_as_text(true)\n\n\nstatic func make_qualified_path(path :String) -> String:\n\tif path.begins_with(\"res://\"):\n\t\treturn path\n\tif path.begins_with(\"//\"):\n\t\treturn path.replace(\"//\", \"res://\")\n\tif path.begins_with(\"/\"):\n\t\treturn \"res:/\" + path\n\treturn path\n\n\nstatic func extract_zip(zip_package :String, dest_path :String) -> GdUnitResult:\n\tvar zip: ZIPReader = ZIPReader.new()\n\tvar err := zip.open(zip_package)\n\tif err != OK:\n\t\treturn GdUnitResult.error(\"Extracting `%s` failed! Please collect the error log and report this. Error Code: %s\" % [zip_package, err])\n\tvar zip_entries: PackedStringArray = zip.get_files()\n\t# Get base path and step over archive folder\n\tvar archive_path := zip_entries[0]\n\tzip_entries.remove_at(0)\n\n\tfor zip_entry in zip_entries:\n\t\tvar new_file_path: String = dest_path + \"/\" + zip_entry.replace(archive_path, \"\")\n\t\tif zip_entry.ends_with(\"/\"):\n\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\tDirAccess.make_dir_recursive_absolute(new_file_path)\n\t\t\tcontinue\n\t\tvar file: FileAccess = FileAccess.open(new_file_path, FileAccess.WRITE)\n\t\tfile.store_buffer(zip.read_file(zip_entry))\n\t@warning_ignore(\"return_value_discarded\")\n\tzip.close()\n\treturn GdUnitResult.success(dest_path)\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitFileAccess.gd.uid",
    "content": "uid://ctwcjgw3wr3db\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitObjectInteractions.gd",
    "content": "class_name GdUnitObjectInteractions\nextends RefCounted\n\n\nstatic func verify(interaction_object :Object, interactions_times :int) -> Variant:\n\tif not _is_mock_or_spy(interaction_object, \"__verify\"):\n\t\treturn interaction_object\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn interaction_object.__do_verify_interactions(interactions_times)\n\n\nstatic func verify_no_interactions(interaction_object :Object) -> GdUnitAssert:\n\tvar __gd_assert := GdUnitAssertImpl.new(\"\")\n\tif not _is_mock_or_spy(interaction_object, \"__verify\"):\n\t\treturn __gd_assert.report_success()\n\t@warning_ignore(\"unsafe_method_access\")\n\tvar __summary :Dictionary = interaction_object.__verify_no_interactions()\n\tif __summary.is_empty():\n\t\treturn __gd_assert.report_success()\n\treturn __gd_assert.report_error(GdAssertMessages.error_no_more_interactions(__summary))\n\n\nstatic func verify_no_more_interactions(interaction_object :Object) -> GdUnitAssert:\n\tvar __gd_assert := GdUnitAssertImpl.new(\"\")\n\tif not _is_mock_or_spy(interaction_object, \"__verify_no_more_interactions\"):\n\t\treturn __gd_assert\n\t@warning_ignore(\"unsafe_method_access\")\n\tvar __summary :Dictionary = interaction_object.__verify_no_more_interactions()\n\tif __summary.is_empty():\n\t\treturn __gd_assert\n\treturn __gd_assert.report_error(GdAssertMessages.error_no_more_interactions(__summary))\n\n\nstatic func reset(interaction_object :Object) -> Object:\n\tif not _is_mock_or_spy(interaction_object, \"__reset\"):\n\t\treturn interaction_object\n\t@warning_ignore(\"unsafe_method_access\")\n\tinteraction_object.__reset_interactions()\n\treturn interaction_object\n\n\nstatic func _is_mock_or_spy(interaction_object :Object, mock_function_signature :String) -> bool:\n\t@warning_ignore(\"unsafe_cast\")\n\tif interaction_object is GDScript and not (interaction_object.get_script() as GDScript).has_method(mock_function_signature):\n\t\tpush_error(\"Error: You try to use a non mock or spy!\")\n\t\treturn false\n\treturn true\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitObjectInteractions.gd.uid",
    "content": "uid://07uhodm4ysvl\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitObjectInteractionsTemplate.gd",
    "content": "\nvar __expected_interactions :int = -1\nvar __saved_interactions := Dictionary()\nvar __verified_interactions := Array()\n\n\nfunc __save_function_interaction(function_args :Array[Variant]) -> void:\n\tvar __matcher := GdUnitArgumentMatchers.to_matcher(function_args, true)\n\tfor __index in __saved_interactions.keys().size():\n\t\tvar __key :Variant = __saved_interactions.keys()[__index]\n\t\tif __matcher.is_match(__key):\n\t\t\t__saved_interactions[__key] += 1\n\t\t\treturn\n\t__saved_interactions[function_args] = 1\n\n\nfunc __is_verify_interactions() -> bool:\n\treturn __expected_interactions != -1\n\n\nfunc __do_verify_interactions(interactions_times :int = 1) -> Object:\n\t__expected_interactions = interactions_times\n\treturn self\n\n\nfunc __verify_interactions(function_args :Array[Variant]) -> void:\n\tvar __summary := Dictionary()\n\tvar __total_interactions := 0\n\tvar __matcher := GdUnitArgumentMatchers.to_matcher(function_args, true)\n\tfor __index in __saved_interactions.keys().size():\n\t\tvar __key :Variant = __saved_interactions.keys()[__index]\n\t\tif __matcher.is_match(__key):\n\t\t\tvar __interactions :int = __saved_interactions.get(__key, 0)\n\t\t\t__total_interactions += __interactions\n\t\t\t__summary[__key] = __interactions\n\t\t\t# add as verified\n\t\t\t__verified_interactions.append(__key)\n\n\tvar __gd_assert := GdUnitAssertImpl.new(\"\")\n\tif __total_interactions != __expected_interactions:\n\t\tvar __expected_summary := {function_args : __expected_interactions}\n\t\tvar __error_message :String\n\t\t# if no __interactions macht collect not verified __interactions for failure report\n\t\tif __summary.is_empty():\n\t\t\tvar __current_summary := __verify_no_more_interactions()\n\t\t\t__error_message = GdAssertMessages.error_validate_interactions(__current_summary, __expected_summary)\n\t\telse:\n\t\t\t__error_message = GdAssertMessages.error_validate_interactions(__summary, __expected_summary)\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\t__gd_assert.report_error(__error_message)\n\telse:\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\t__gd_assert.report_success()\n\t__expected_interactions = -1\n\n\nfunc __verify_no_interactions() -> Dictionary:\n\tvar __summary := Dictionary()\n\tif not __saved_interactions.is_empty():\n\t\tfor __index in __saved_interactions.keys().size():\n\t\t\tvar func_call :Variant = __saved_interactions.keys()[__index]\n\t\t\t__summary[func_call] = __saved_interactions[func_call]\n\treturn __summary\n\n\nfunc __verify_no_more_interactions() -> Dictionary:\n\tvar __summary := Dictionary()\n\tvar called_functions :Array[Variant] = __saved_interactions.keys()\n\tif called_functions != __verified_interactions:\n\t\t# collect the not verified functions\n\t\tvar called_but_not_verified := called_functions.duplicate()\n\t\tfor __index in __verified_interactions.size():\n\t\t\tcalled_but_not_verified.erase(__verified_interactions[__index])\n\n\t\tfor __index in called_but_not_verified.size():\n\t\t\tvar not_verified :Variant = called_but_not_verified[__index]\n\t\t\t__summary[not_verified] = __saved_interactions[not_verified]\n\treturn __summary\n\n\nfunc __reset_interactions() -> void:\n\t__saved_interactions.clear()\n\n\nfunc __filter_vargs(arg_values :Array[Variant]) -> Array[Variant]:\n\tvar filtered :Array[Variant] = []\n\tfor __index in arg_values.size():\n\t\tvar arg :Variant = arg_values[__index]\n\t\tif typeof(arg) == TYPE_STRING and arg == GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE:\n\t\t\tcontinue\n\t\tfiltered.append(arg)\n\treturn filtered\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitObjectInteractionsTemplate.gd.uid",
    "content": "uid://d30lgjpg21aut\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitProperty.gd",
    "content": "class_name GdUnitProperty\nextends RefCounted\n\n\nvar _name :String\nvar _help :String\nvar _type :int\nvar _value :Variant\nvar _value_set :PackedStringArray\nvar _default :Variant\n\n\nfunc _init(p_name :String, p_type :int, p_value :Variant, p_default_value :Variant, p_help :=\"\", p_value_set := PackedStringArray()) -> void:\n\t_name = p_name\n\t_type = p_type\n\t_value = p_value\n\t_value_set = p_value_set\n\t_default = p_default_value\n\t_help = p_help\n\n\nfunc name() -> String:\n\treturn _name\n\n\nfunc type() -> int:\n\treturn _type\n\n\nfunc value() -> Variant:\n\treturn _value\n\n\nfunc value_as_string() -> String:\n\treturn _value\n\n\nfunc value_set() -> PackedStringArray:\n\treturn _value_set\n\n\nfunc is_selectable_value() -> bool:\n\treturn not _value_set.is_empty()\n\n\nfunc set_value(p_value :Variant) -> void:\n\tmatch _type:\n\t\tTYPE_STRING:\n\t\t\t_value = str(p_value)\n\t\tTYPE_BOOL:\n\t\t\t_value = convert(p_value, TYPE_BOOL)\n\t\tTYPE_INT:\n\t\t\t_value = convert(p_value, TYPE_INT)\n\t\tTYPE_FLOAT:\n\t\t\t_value = convert(p_value, TYPE_FLOAT)\n\t\t_:\n\t\t\t_value = p_value\n\n\nfunc default() -> Variant:\n\treturn _default\n\n\nfunc category() -> String:\n\tvar elements := _name.split(\"/\")\n\tif elements.size() > 3:\n\t\treturn elements[2]\n\treturn \"\"\n\n\nfunc help() -> String:\n\treturn _help\n\n\nfunc _to_string() -> String:\n\treturn \"%-64s %-10s %-10s (%s) help:%s set:%s\" % [name(), type(), value(), default(), help(), _value_set]\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitProperty.gd.uid",
    "content": "uid://c0nnmmmyuv18v\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitResult.gd",
    "content": "class_name GdUnitResult\nextends RefCounted\n\nenum {\n\tSUCCESS,\n\tWARN,\n\tERROR,\n\tEMPTY\n}\n\nvar _state: int\nvar _warn_message := \"\"\nvar _error_message := \"\"\nvar _value :Variant = null\n\n\nstatic func empty() -> GdUnitResult:\n\tvar result := GdUnitResult.new()\n\tresult._state = EMPTY\n\treturn result\n\n\nstatic func success(p_value :Variant) -> GdUnitResult:\n\tassert(p_value != null, \"The value must not be NULL\")\n\tvar result := GdUnitResult.new()\n\tresult._value = p_value\n\tresult._state = SUCCESS\n\treturn result\n\n\nstatic func warn(p_warn_message :String, p_value :Variant = null) -> GdUnitResult:\n\tassert(not p_warn_message.is_empty()) #,\"The message must not be empty\")\n\tvar result := GdUnitResult.new()\n\tresult._value = p_value\n\tresult._warn_message = p_warn_message\n\tresult._state = WARN\n\treturn result\n\n\nstatic func error(p_error_message :String) -> GdUnitResult:\n\tassert(not p_error_message.is_empty(), \"The message must not be empty\")\n\tvar result := GdUnitResult.new()\n\tresult._value = null\n\tresult._error_message = p_error_message\n\tresult._state = ERROR\n\treturn result\n\n\nfunc is_success() -> bool:\n\treturn _state == SUCCESS\n\n\nfunc is_warn() -> bool:\n\treturn _state == WARN\n\n\nfunc is_error() -> bool:\n\treturn _state == ERROR\n\n\nfunc is_empty() -> bool:\n\treturn _state == EMPTY\n\n\nfunc value() -> Variant:\n\treturn _value\n\n\nfunc value_as_string() -> String:\n\treturn _value\n\n\nfunc or_else(p_value :Variant) -> Variant:\n\tif not is_success():\n\t\treturn p_value\n\treturn value()\n\n\nfunc error_message() -> String:\n\treturn _error_message\n\n\nfunc warn_message() -> String:\n\treturn _warn_message\n\n\nfunc _to_string() -> String:\n\treturn str(GdUnitResult.serialize(self))\n\n\nstatic func serialize(result :GdUnitResult) -> Dictionary:\n\tif result == null:\n\t\tpush_error(\"Can't serialize a Null object from type GdUnitResult\")\n\treturn {\n\t\t\"state\" : result._state,\n\t\t\"value\" : var_to_str(result._value),\n\t\t\"warn_msg\" : result._warn_message,\n\t\t\"err_msg\" : result._error_message\n\t}\n\n\nstatic func deserialize(config :Dictionary) -> GdUnitResult:\n\tvar result := GdUnitResult.new()\n\tvar cfg_value: String = config.get(\"value\", \"\")\n\tresult._value = str_to_var(cfg_value)\n\tresult._warn_message = config.get(\"warn_msg\", null)\n\tresult._error_message = config.get(\"err_msg\", null)\n\tresult._state = config.get(\"state\")\n\treturn result\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitResult.gd.uid",
    "content": "uid://dyg2klxre6ly7\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitRunner.gd",
    "content": "extends Node\n\n@onready var _client :GdUnitTcpClient = $GdUnitTcpClient\n@onready var _executor :GdUnitTestSuiteExecutor = GdUnitTestSuiteExecutor.new()\n\nenum {\n\tINIT,\n\tRUN,\n\tSTOP,\n\tEXIT\n}\n\nconst GDUNIT_RUNNER = \"GdUnitRunner\"\n\nvar _config := GdUnitRunnerConfig.new()\nvar _test_suites_to_process :Array[Node]\nvar _state :int = INIT\nvar _cs_executor :RefCounted\n\n\nfunc _init() -> void:\n\t# minimize scene window checked debug mode\n\tif OS.get_cmdline_args().size() == 1:\n\t\tDisplayServer.window_set_title(\"GdUnit4 Runner (Debug Mode)\")\n\telse:\n\t\tDisplayServer.window_set_title(\"GdUnit4 Runner (Release Mode)\")\n\tDisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_MINIMIZED)\n\t# store current runner instance to engine meta data to can be access in as a singleton\n\tEngine.set_meta(GDUNIT_RUNNER, self)\n\t_cs_executor = GdUnit4CSharpApiLoader.create_executor(self)\n\n\nfunc _ready() -> void:\n\tvar config_result := _config.load_config()\n\tif config_result.is_error():\n\t\tpush_error(config_result.error_message())\n\t\t_state = EXIT\n\t\treturn\n\t@warning_ignore(\"return_value_discarded\")\n\t_client.connect(\"connection_failed\", _on_connection_failed)\n\t@warning_ignore(\"return_value_discarded\")\n\tGdUnitSignals.instance().gdunit_event.connect(_on_gdunit_event)\n\tvar result := _client.start(\"127.0.0.1\", _config.server_port())\n\tif result.is_error():\n\t\tpush_error(result.error_message())\n\t\treturn\n\t_state = INIT\n\n\nfunc _on_connection_failed(message :String) -> void:\n\tprints(\"_on_connection_failed\", message, _test_suites_to_process)\n\t_state = STOP\n\n\nfunc _notification(what :int) -> void:\n\t#prints(\"GdUnitRunner\", self, GdObjects.notification_as_string(what))\n\tif what == NOTIFICATION_PREDELETE:\n\t\tEngine.remove_meta(GDUNIT_RUNNER)\n\n\nfunc _process(_delta :float) -> void:\n\tmatch _state:\n\t\tINIT:\n\t\t\t# wait until client is connected to the GdUnitServer\n\t\t\tif _client.is_client_connected():\n\t\t\t\tvar time := LocalTime.now()\n\t\t\t\tprints(\"Scan for test suites.\")\n\t\t\t\t_test_suites_to_process = load_test_suites()\n\t\t\t\tprints(\"Scanning of %d test suites took\" % _test_suites_to_process.size(), time.elapsed_since())\n\t\t\t\tgdUnitInit()\n\t\t\t\t_state = RUN\n\t\tRUN:\n\t\t\t# all test suites executed\n\t\t\tif _test_suites_to_process.is_empty():\n\t\t\t\t_state = STOP\n\t\t\telse:\n\t\t\t\t# process next test suite\n\t\t\t\tset_process(false)\n\t\t\t\tvar test_suite :Node = _test_suites_to_process.pop_front()\n\t\t\t\t@warning_ignore(\"unsafe_method_access\")\n\t\t\t\tif _cs_executor != null and _cs_executor.IsExecutable(test_suite):\n\t\t\t\t\t@warning_ignore(\"unsafe_method_access\")\n\t\t\t\t\t_cs_executor.Execute(test_suite)\n\t\t\t\t\t@warning_ignore(\"unsafe_property_access\")\n\t\t\t\t\tawait _cs_executor.ExecutionCompleted\n\t\t\t\telse:\n\t\t\t\t\tawait _executor.execute(test_suite as GdUnitTestSuite)\n\t\t\t\tset_process(true)\n\t\tSTOP:\n\t\t\t_state = EXIT\n\t\t\t# give the engine small amount time to finish the rpc\n\t\t\t_on_gdunit_event(GdUnitStop.new())\n\t\t\tawait get_tree().create_timer(0.1).timeout\n\t\t\tawait get_tree().process_frame\n\t\t\tget_tree().quit(0)\n\n\nfunc load_test_suites() -> Array[Node]:\n\tvar to_execute := _config.to_execute()\n\tif to_execute.is_empty():\n\t\tprints(\"No tests selected to execute!\")\n\t\t_state = EXIT\n\t\treturn []\n\t# scan for the requested test suites\n\tvar test_suites :Array[Node] = []\n\tvar _scanner := GdUnitTestSuiteScanner.new()\n\tfor resource_path :String in to_execute.keys():\n\t\tvar selected_tests :PackedStringArray = to_execute.get(resource_path)\n\t\tvar scanned_suites := _scanner.scan(resource_path)\n\t\t_filter_test_case(scanned_suites, selected_tests)\n\t\ttest_suites += scanned_suites\n\treturn test_suites\n\n\nfunc gdUnitInit() -> void:\n\t#enable_manuall_polling()\n\tsend_message(\"Scanned %d test suites\" % _test_suites_to_process.size())\n\tvar total_count := _collect_test_case_count(_test_suites_to_process)\n\t_on_gdunit_event(GdUnitInit.new(_test_suites_to_process.size(), total_count))\n\tif not GdUnitSettings.is_test_discover_enabled():\n\t\tfor test_suite in _test_suites_to_process:\n\t\t\tsend_test_suite(test_suite)\n\n\nfunc _filter_test_case(test_suites :Array[Node], included_tests :PackedStringArray) -> void:\n\tif included_tests.is_empty():\n\t\treturn\n\tfor test_suite in test_suites:\n\t\tfor test_case in test_suite.get_children():\n\t\t\t_do_filter_test_case(test_suite, test_case, included_tests)\n\n\nfunc _do_filter_test_case(test_suite :Node, test_case :Node, included_tests :PackedStringArray) -> void:\n\tfor included_test in included_tests:\n\t\tvar test_meta :PackedStringArray = included_test.split(\":\")\n\t\tvar test_name := test_meta[0]\n\t\tif test_case.get_name() == test_name:\n\t\t\t# we have a paremeterized test selection\n\t\t\tif test_meta.size() > 1:\n\t\t\t\tvar test_param_index := test_meta[1]\n\t\t\t\t@warning_ignore(\"unsafe_method_access\")\n\t\t\t\ttest_case.set_test_parameter_index(test_param_index.to_int())\n\t\t\treturn\n\t# the test is filtered out\n\ttest_suite.remove_child(test_case)\n\ttest_case.free()\n\n\nfunc _collect_test_case_count(testSuites :Array[Node]) -> int:\n\tvar total :int = 0\n\tfor test_suite in testSuites:\n\t\ttotal += test_suite.get_child_count()\n\treturn total\n\n\n# RPC send functions\nfunc send_message(message :String) -> void:\n\t_client.rpc_send(RPCMessage.of(message))\n\n\nfunc send_test_suite(test_suite :Node) -> void:\n\t_client.rpc_send(RPCGdUnitTestSuite.of(test_suite))\n\n\nfunc _on_gdunit_event(event :GdUnitEvent) -> void:\n\t_client.rpc_send(RPCGdUnitEvent.of(event))\n\n\n# Event bridge from C# GdUnit4.ITestEventListener.cs\nfunc PublishEvent(data :Dictionary) -> void:\n\tvar event := GdUnitEvent.new().deserialize(data)\n\t_client.rpc_send(RPCGdUnitEvent.of(event))\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitRunner.gd.uid",
    "content": "uid://c7wu0u21yncv5\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitRunner.tscn",
    "content": "[gd_scene load_steps=3 format=3 uid=\"uid://belidlfknh74r\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/gdUnit4/src/core/GdUnitRunner.gd\" id=\"1\"]\n[ext_resource type=\"Script\" path=\"res://addons/gdUnit4/src/network/GdUnitTcpClient.gd\" id=\"2\"]\n\n[node name=\"Control\" type=\"Node\"]\nscript = ExtResource(\"1\")\n\n[node name=\"GdUnitTcpClient\" type=\"Node\" parent=\".\"]\nscript = ExtResource(\"2\")\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitRunnerConfig.gd",
    "content": "class_name GdUnitRunnerConfig\nextends Resource\n\nconst GdUnitTools := preload(\"res://addons/gdUnit4/src/core/GdUnitTools.gd\")\n\nconst CONFIG_VERSION = \"1.0\"\nconst VERSION = \"version\"\nconst INCLUDED = \"included\"\nconst SKIPPED = \"skipped\"\nconst SERVER_PORT = \"server_port\"\nconst EXIT_FAIL_FAST = \"exit_on_first_fail\"\n\nconst CONFIG_FILE = \"res://addons/gdUnit4/GdUnitRunner.cfg\"\n\nvar _config := {\n\t\tVERSION : CONFIG_VERSION,\n\t\t# a set of directories or testsuite paths as key and a optional set of testcases as values\n\t\tINCLUDED :  Dictionary(),\n\t\t# a set of skipped directories or testsuite paths\n\t\tSKIPPED : Dictionary(),\n\t\t# the port of running test server for this session\n\t\tSERVER_PORT : -1\n\t}\n\n\nfunc clear() -> GdUnitRunnerConfig:\n\t_config[INCLUDED] = Dictionary()\n\t_config[SKIPPED] = Dictionary()\n\treturn self\n\n\nfunc set_server_port(port: int) -> GdUnitRunnerConfig:\n\t_config[SERVER_PORT] = port\n\treturn self\n\n\nfunc server_port() -> int:\n\treturn _config.get(SERVER_PORT, -1)\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc self_test() -> GdUnitRunnerConfig:\n\tadd_test_suite(\"res://addons/gdUnit4/test/\")\n\tadd_test_suite(\"res://addons/gdUnit4/mono/test/\")\n\treturn self\n\n\nfunc add_test_suite(p_resource_path: String) -> GdUnitRunnerConfig:\n\tvar to_execute_ := to_execute()\n\tto_execute_[p_resource_path] = to_execute_.get(p_resource_path, PackedStringArray())\n\treturn self\n\n\nfunc add_test_suites(resource_paths: PackedStringArray) -> GdUnitRunnerConfig:\n\tfor resource_path_ in resource_paths:\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tadd_test_suite(resource_path_)\n\treturn self\n\n\nfunc add_test_case(p_resource_path: String, test_name: String, test_param_index: int = -1) -> GdUnitRunnerConfig:\n\tvar to_execute_ := to_execute()\n\tvar test_cases: PackedStringArray = to_execute_.get(p_resource_path, PackedStringArray())\n\tif test_param_index != -1:\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\ttest_cases.append(\"%s:%d\" % [test_name, test_param_index])\n\telse:\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\ttest_cases.append(test_name)\n\tto_execute_[p_resource_path] = test_cases\n\treturn self\n\n\n# supports full path or suite name with optional test case name\n# <test_suite_name|path>[:<test_case_name>]\n# '/path/path', res://path/path', 'res://path/path/testsuite.gd' or 'testsuite'\n# 'res://path/path/testsuite.gd:test_case' or 'testsuite:test_case'\nfunc skip_test_suite(value: String) -> GdUnitRunnerConfig:\n\tvar parts: PackedStringArray = GdUnitFileAccess.make_qualified_path(value).rsplit(\":\")\n\tif parts[0] == \"res\":\n\t\tparts.remove_at(0)\n\tparts[0] = GdUnitFileAccess.make_qualified_path(parts[0])\n\tmatch parts.size():\n\t\t1:\n\t\t\tskipped()[parts[0]] = PackedStringArray()\n\t\t2:\n\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t_skip_test_case(parts[0], parts[1])\n\treturn self\n\n\nfunc skip_test_suites(resource_paths: PackedStringArray) -> GdUnitRunnerConfig:\n\tfor resource_path_ in resource_paths:\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tskip_test_suite(resource_path_)\n\treturn self\n\n\nfunc _skip_test_case(p_resource_path: String, test_name: String) -> GdUnitRunnerConfig:\n\tvar to_ignore := skipped()\n\tvar test_cases: PackedStringArray = to_ignore.get(p_resource_path, PackedStringArray())\n\t@warning_ignore(\"return_value_discarded\")\n\ttest_cases.append(test_name)\n\tto_ignore[p_resource_path] = test_cases\n\treturn self\n\n\n# Dictionary[String, Dictionary[String, PackedStringArray]]\nfunc to_execute() -> Dictionary:\n\treturn _config.get(INCLUDED, {\"res://\" : PackedStringArray()})\n\n\nfunc skipped() -> Dictionary:\n\treturn _config.get(SKIPPED, {})\n\n\nfunc save_config(path: String = CONFIG_FILE) -> GdUnitResult:\n\tvar file := FileAccess.open(path, FileAccess.WRITE)\n\tif file == null:\n\t\tvar error := FileAccess.get_open_error()\n\t\treturn GdUnitResult.error(\"Can't write test runner configuration '%s'! %s\" % [path, error_string(error)])\n\t_config[VERSION] = CONFIG_VERSION\n\tfile.store_string(JSON.stringify(_config))\n\treturn GdUnitResult.success(path)\n\n\nfunc load_config(path: String = CONFIG_FILE) -> GdUnitResult:\n\tif not FileAccess.file_exists(path):\n\t\treturn GdUnitResult.error(\"Can't find test runner configuration '%s'! Please select a test to run.\" % path)\n\tvar file := FileAccess.open(path, FileAccess.READ)\n\tif file == null:\n\t\tvar error := FileAccess.get_open_error()\n\t\treturn GdUnitResult.error(\"Can't load test runner configuration '%s'! ERROR: %s.\" % [path, error_string(error)])\n\tvar content := file.get_as_text()\n\tif not content.is_empty() and content[0] == '{':\n\t\t# Parse as json\n\t\tvar test_json_conv := JSON.new()\n\t\tvar error := test_json_conv.parse(content)\n\t\tif error != OK:\n\t\t\treturn GdUnitResult.error(\"The runner configuration '%s' is invalid! The format is changed please delete it manually and start a new test run.\" % path)\n\t\t_config = test_json_conv.get_data()\n\t\tif not _config.has(VERSION):\n\t\t\treturn GdUnitResult.error(\"The runner configuration '%s' is invalid! The format is changed please delete it manually and start a new test run.\" % path)\n\t\tfix_value_types()\n\treturn GdUnitResult.success(path)\n\n\n@warning_ignore(\"unsafe_cast\")\nfunc fix_value_types() -> void:\n\t# fix float value to int json stores all numbers as float\n\tvar server_port_: int = _config.get(SERVER_PORT, -1)\n\t_config[SERVER_PORT] = server_port_\n\tconvert_Array_to_PackedStringArray(_config[INCLUDED] as Dictionary)\n\tconvert_Array_to_PackedStringArray(_config[SKIPPED] as Dictionary)\n\n\nfunc convert_Array_to_PackedStringArray(data: Dictionary) -> void:\n\tfor key in data.keys() as Array[String]:\n\t\tvar values :Array = data[key]\n\t\tdata[key] = PackedStringArray(values)\n\n\nfunc _to_string() -> String:\n\treturn str(_config)\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitRunnerConfig.gd.uid",
    "content": "uid://dhcoabne4hy87\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd",
    "content": "# This class provides a runner for scense to simulate interactions like keyboard or mouse\nclass_name GdUnitSceneRunnerImpl\nextends GdUnitSceneRunner\n\n\nvar GdUnitFuncAssertImpl: GDScript = ResourceLoader.load(\"res://addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd\", \"GDScript\", ResourceLoader.CACHE_MODE_REUSE)\n\n\n# mapping of mouse buttons and his masks\nconst MAP_MOUSE_BUTTON_MASKS := {\n\tMOUSE_BUTTON_LEFT : MOUSE_BUTTON_MASK_LEFT,\n\tMOUSE_BUTTON_RIGHT : MOUSE_BUTTON_MASK_RIGHT,\n\tMOUSE_BUTTON_MIDDLE : MOUSE_BUTTON_MASK_MIDDLE,\n\t# https://github.com/godotengine/godot/issues/73632\n\tMOUSE_BUTTON_WHEEL_UP : 1 << (MOUSE_BUTTON_WHEEL_UP - 1),\n\tMOUSE_BUTTON_WHEEL_DOWN : 1 << (MOUSE_BUTTON_WHEEL_DOWN - 1),\n\tMOUSE_BUTTON_XBUTTON1 : MOUSE_BUTTON_MASK_MB_XBUTTON1,\n\tMOUSE_BUTTON_XBUTTON2 : MOUSE_BUTTON_MASK_MB_XBUTTON2,\n}\n\nvar _is_disposed := false\nvar _current_scene: Node = null\nvar _awaiter: GdUnitAwaiter = GdUnitAwaiter.new()\nvar _verbose: bool\nvar _simulate_start_time: LocalTime\nvar _last_input_event: InputEvent = null\nvar _mouse_button_on_press := []\nvar _key_on_press := []\nvar _action_on_press := []\nvar _curent_mouse_position: Vector2\n# holds the touch position for each touch index\n# { index: int = position: Vector2}\nvar _current_touch_position: Dictionary = {}\n# holds the curretn touch drag position\nvar _current_touch_drag_position: Vector2 = Vector2.ZERO\n\n# time factor settings\nvar _time_factor := 1.0\nvar _saved_iterations_per_second: float\nvar _scene_auto_free := false\n\n\nfunc _init(p_scene: Variant, p_verbose: bool, p_hide_push_errors := false) -> void:\n\t_verbose = p_verbose\n\t_saved_iterations_per_second = Engine.get_physics_ticks_per_second()\n\t@warning_ignore(\"return_value_discarded\")\n\tset_time_factor(1)\n\t# handle scene loading by resource path\n\tif typeof(p_scene) == TYPE_STRING:\n\t\t@warning_ignore(\"unsafe_cast\")\n\t\tif !ResourceLoader.exists(p_scene as String):\n\t\t\tif not p_hide_push_errors:\n\t\t\t\tpush_error(\"GdUnitSceneRunner: Can't load scene by given resource path: '%s'. The resource does not exists.\" % p_scene)\n\t\t\treturn\n\t\tif !str(p_scene).ends_with(\".tscn\") and !str(p_scene).ends_with(\".scn\") and !str(p_scene).begins_with(\"uid://\"):\n\t\t\tif not p_hide_push_errors:\n\t\t\t\tpush_error(\"GdUnitSceneRunner: The given resource: '%s'. is not a scene.\" % p_scene)\n\t\t\treturn\n\t\t@warning_ignore(\"unsafe_cast\")\n\t\t_current_scene = (load(p_scene as String) as PackedScene).instantiate()\n\t\t_scene_auto_free = true\n\telse:\n\t\t# verify we have a node instance\n\t\tif not p_scene is Node:\n\t\t\tif not p_hide_push_errors:\n\t\t\t\tpush_error(\"GdUnitSceneRunner: The given instance '%s' is not a Node.\" % p_scene)\n\t\t\treturn\n\t\t_current_scene = p_scene\n\tif _current_scene == null:\n\t\tif not p_hide_push_errors:\n\t\t\tpush_error(\"GdUnitSceneRunner: Scene must be not null!\")\n\t\treturn\n\n\t_scene_tree().root.add_child(_current_scene)\n\t# do finally reset all open input events when the scene is removed\n\t@warning_ignore(\"return_value_discarded\")\n\t_scene_tree().root.child_exiting_tree.connect(func f(child :Node) -> void:\n\t\tif child == _current_scene:\n\t\t\t# we need to disable the processing to avoid input flush buffer errors\n\t\t\t_current_scene.process_mode = Node.PROCESS_MODE_DISABLED\n\t\t\t_reset_input_to_default()\n\t)\n\t_simulate_start_time = LocalTime.now()\n\t# we need to set inital a valid window otherwise the warp_mouse() is not handled\n\tDisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)\n\t# set inital mouse pos to 0,0\n\tvar max_iteration_to_wait := 0\n\twhile get_global_mouse_position() != Vector2.ZERO and max_iteration_to_wait < 100:\n\t\tInput.warp_mouse(Vector2.ZERO)\n\t\tmax_iteration_to_wait += 1\n\n\nfunc _notification(what: int) -> void:\n\tif what == NOTIFICATION_PREDELETE and is_instance_valid(self):\n\t\t# reset time factor to normal\n\t\t__deactivate_time_factor()\n\t\tif is_instance_valid(_current_scene):\n\t\t\t_scene_tree().root.remove_child(_current_scene)\n\t\t\t# do only free scenes instanciated by this runner\n\t\t\tif _scene_auto_free:\n\t\t\t\t_current_scene.free()\n\t\t_is_disposed = true\n\t\t_current_scene = null\n\n\nfunc _scene_tree() -> SceneTree:\n\treturn Engine.get_main_loop() as SceneTree\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc simulate_action_pressed(action: String) -> GdUnitSceneRunner:\n\tsimulate_action_press(action)\n\tsimulate_action_release(action)\n\treturn self\n\n\nfunc simulate_action_press(action: String) -> GdUnitSceneRunner:\n\t__print_current_focus()\n\tvar event := InputEventAction.new()\n\tevent.pressed = true\n\tevent.action = action\n\tif Engine.get_version_info().hex >= 0x40300:\n\t\t@warning_ignore(\"unsafe_property_access\")\n\t\tevent.event_index = InputMap.get_actions().find(action)\n\t_action_on_press.append(action)\n\treturn _handle_input_event(event)\n\n\nfunc simulate_action_release(action: String) -> GdUnitSceneRunner:\n\t__print_current_focus()\n\tvar event := InputEventAction.new()\n\tevent.pressed = false\n\tevent.action = action\n\tif Engine.get_version_info().hex >= 0x40300:\n\t\t@warning_ignore(\"unsafe_property_access\")\n\t\tevent.event_index = InputMap.get_actions().find(action)\n\t_action_on_press.erase(action)\n\treturn _handle_input_event(event)\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc simulate_key_pressed(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner:\n\tsimulate_key_press(key_code, shift_pressed, ctrl_pressed)\n\tawait _scene_tree().process_frame\n\tsimulate_key_release(key_code, shift_pressed, ctrl_pressed)\n\treturn self\n\n\nfunc simulate_key_press(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner:\n\t__print_current_focus()\n\tvar event := InputEventKey.new()\n\tevent.pressed = true\n\tevent.keycode = key_code as Key\n\tevent.physical_keycode = key_code as Key\n\tevent.alt_pressed = key_code == KEY_ALT\n\tevent.shift_pressed = shift_pressed or key_code == KEY_SHIFT\n\tevent.ctrl_pressed = ctrl_pressed or key_code == KEY_CTRL\n\t_apply_input_modifiers(event)\n\t_key_on_press.append(key_code)\n\treturn _handle_input_event(event)\n\n\nfunc simulate_key_release(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner:\n\t__print_current_focus()\n\tvar event := InputEventKey.new()\n\tevent.pressed = false\n\tevent.keycode = key_code as Key\n\tevent.physical_keycode = key_code as Key\n\tevent.alt_pressed = key_code == KEY_ALT\n\tevent.shift_pressed = shift_pressed or key_code == KEY_SHIFT\n\tevent.ctrl_pressed = ctrl_pressed or key_code == KEY_CTRL\n\t_apply_input_modifiers(event)\n\t_key_on_press.erase(key_code)\n\treturn _handle_input_event(event)\n\n\nfunc set_mouse_pos(pos: Vector2) -> GdUnitSceneRunner:\n\treturn set_mouse_position(pos)\n\n\nfunc set_mouse_position(pos: Vector2) -> GdUnitSceneRunner:\n\tvar event := InputEventMouseMotion.new()\n\tevent.position = pos\n\tevent.global_position = get_global_mouse_position()\n\t_apply_input_modifiers(event)\n\treturn _handle_input_event(event)\n\n\nfunc get_mouse_position() -> Vector2:\n\tif _last_input_event is InputEventMouse:\n\t\treturn (_last_input_event as InputEventMouse).position\n\tvar current_scene := scene()\n\tif current_scene != null:\n\t\treturn current_scene.get_viewport().get_mouse_position()\n\treturn Vector2.ZERO\n\n\nfunc get_global_mouse_position() -> Vector2:\n\treturn (Engine.get_main_loop() as SceneTree).root.get_mouse_position()\n\n\nfunc simulate_mouse_move(position: Vector2) -> GdUnitSceneRunner:\n\tvar event := InputEventMouseMotion.new()\n\tevent.position = position\n\tevent.relative = position - get_mouse_position()\n\tevent.global_position = get_global_mouse_position()\n\t_apply_input_mouse_mask(event)\n\t_apply_input_modifiers(event)\n\treturn _handle_input_event(event)\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc simulate_mouse_move_relative(relative: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner:\n\tvar tween := _scene_tree().create_tween()\n\t_curent_mouse_position = get_mouse_position()\n\tvar final_position := _curent_mouse_position + relative\n\ttween.tween_property(self, \"_curent_mouse_position\", final_position, time).set_trans(trans_type)\n\ttween.play()\n\n\twhile not get_mouse_position().is_equal_approx(final_position):\n\t\tsimulate_mouse_move(_curent_mouse_position)\n\t\tawait _scene_tree().process_frame\n\treturn self\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc simulate_mouse_move_absolute(position: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner:\n\tvar tween := _scene_tree().create_tween()\n\t_curent_mouse_position = get_mouse_position()\n\ttween.tween_property(self, \"_curent_mouse_position\", position, time).set_trans(trans_type)\n\ttween.play()\n\n\twhile not get_mouse_position().is_equal_approx(position):\n\t\tsimulate_mouse_move(_curent_mouse_position)\n\t\tawait _scene_tree().process_frame\n\treturn self\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc simulate_mouse_button_pressed(button_index: MouseButton, double_click := false) -> GdUnitSceneRunner:\n\tsimulate_mouse_button_press(button_index, double_click)\n\tsimulate_mouse_button_release(button_index)\n\treturn self\n\n\nfunc simulate_mouse_button_press(button_index: MouseButton, double_click := false) -> GdUnitSceneRunner:\n\tvar event := InputEventMouseButton.new()\n\tevent.button_index = button_index\n\tevent.pressed = true\n\tevent.double_click = double_click\n\t_apply_input_mouse_position(event)\n\t_apply_input_mouse_mask(event)\n\t_apply_input_modifiers(event)\n\t_mouse_button_on_press.append(button_index)\n\treturn _handle_input_event(event)\n\n\nfunc simulate_mouse_button_release(button_index: MouseButton) -> GdUnitSceneRunner:\n\tvar event := InputEventMouseButton.new()\n\tevent.button_index = button_index\n\tevent.pressed = false\n\t_apply_input_mouse_position(event)\n\t_apply_input_mouse_mask(event)\n\t_apply_input_modifiers(event)\n\t_mouse_button_on_press.erase(button_index)\n\treturn _handle_input_event(event)\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc simulate_screen_touch_pressed(index: int, position: Vector2, double_tap := false) -> GdUnitSceneRunner:\n\tsimulate_screen_touch_press(index, position, double_tap)\n\tsimulate_screen_touch_release(index)\n\treturn self\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc simulate_screen_touch_press(index: int, position: Vector2, double_tap := false) -> GdUnitSceneRunner:\n\tif is_emulate_mouse_from_touch():\n\t\t# we need to simulate in addition to the touch the mouse events\n\t\tset_mouse_pos(position)\n\t\tsimulate_mouse_button_press(MOUSE_BUTTON_LEFT)\n\t# push touch press event at position\n\tvar event := InputEventScreenTouch.new()\n\tevent.window_id = scene().get_window().get_window_id()\n\tevent.index = index\n\tevent.position = position\n\tevent.double_tap = double_tap\n\tevent.pressed = true\n\t_current_scene.get_viewport().push_input(event)\n\t# save current drag position by index\n\t_current_touch_position[index] = position\n\treturn self\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc simulate_screen_touch_release(index: int, double_tap := false) -> GdUnitSceneRunner:\n\tif is_emulate_mouse_from_touch():\n\t\t# we need to simulate in addition to the touch the mouse events\n\t\tsimulate_mouse_button_release(MOUSE_BUTTON_LEFT)\n\t# push touch release event at position\n\tvar event := InputEventScreenTouch.new()\n\tevent.window_id = scene().get_window().get_window_id()\n\tevent.index = index\n\tevent.position = get_screen_touch_drag_position(index)\n\tevent.pressed = false\n\tevent.double_tap = (_last_input_event as InputEventScreenTouch).double_tap if _last_input_event is InputEventScreenTouch else double_tap\n\t_current_scene.get_viewport().push_input(event)\n\treturn self\n\n\nfunc simulate_screen_touch_drag_relative(index: int, relative: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner:\n\tvar current_position: Vector2 = _current_touch_position[index]\n\treturn await _do_touch_drag_at(index, current_position + relative, time, trans_type)\n\n\nfunc simulate_screen_touch_drag_absolute(index: int, position: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner:\n\treturn await _do_touch_drag_at(index, position, time, trans_type)\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc simulate_screen_touch_drag_drop(index: int, position: Vector2, drop_position: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner:\n\tsimulate_screen_touch_press(index, position)\n\treturn await _do_touch_drag_at(index, drop_position, time, trans_type)\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc simulate_screen_touch_drag(index: int, position: Vector2) -> GdUnitSceneRunner:\n\tif is_emulate_mouse_from_touch():\n\t\tsimulate_mouse_move(position)\n\tvar event := InputEventScreenDrag.new()\n\tevent.window_id = scene().get_window().get_window_id()\n\tevent.index = index\n\tevent.position = position\n\tevent.relative = _get_screen_touch_drag_position_or_default(index, position) - position\n\tevent.velocity = event.relative / _scene_tree().root.get_process_delta_time()\n\tevent.pressure = 1.0\n\t_current_touch_position[index] = position\n\t_current_scene.get_viewport().push_input(event)\n\treturn self\n\n\nfunc get_screen_touch_drag_position(index: int) -> Vector2:\n\tif _current_touch_position.has(index):\n\t\treturn _current_touch_position[index]\n\tpush_error(\"No touch drag position for index '%d' is set!\" % index)\n\treturn Vector2.ZERO\n\n\nfunc is_emulate_mouse_from_touch() -> bool:\n\treturn ProjectSettings.get_setting(\"input_devices/pointing/emulate_mouse_from_touch\", true)\n\n\nfunc _get_screen_touch_drag_position_or_default(index: int, default_position: Vector2) -> Vector2:\n\tif _current_touch_position.has(index):\n\t\treturn _current_touch_position[index]\n\treturn default_position\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc _do_touch_drag_at(index: int, drag_position: Vector2, time: float, trans_type: Tween.TransitionType) -> GdUnitSceneRunner:\n\t# start draging\n\tvar event := InputEventScreenDrag.new()\n\tevent.window_id = scene().get_window().get_window_id()\n\tevent.index = index\n\tevent.position = get_screen_touch_drag_position(index)\n\tevent.pressure = 1.0\n\t_current_touch_drag_position = event.position\n\n\tvar tween := _scene_tree().create_tween()\n\ttween.tween_property(self, \"_current_touch_drag_position\", drag_position, time).set_trans(trans_type)\n\ttween.play()\n\n\twhile not _current_touch_drag_position.is_equal_approx(drag_position):\n\t\tif is_emulate_mouse_from_touch():\n\t\t\t# we need to simulate in addition to the drag the mouse move events\n\t\t\tsimulate_mouse_move(event.position)\n\t\t# send touche drag event to new position\n\t\tevent.relative = _current_touch_drag_position - event.position\n\t\tevent.velocity = event.relative / _scene_tree().root.get_process_delta_time()\n\t\tevent.position = _current_touch_drag_position\n\t\t_current_scene.get_viewport().push_input(event)\n\t\tawait _scene_tree().process_frame\n\n\t# finaly drop it\n\tif is_emulate_mouse_from_touch():\n\t\tsimulate_mouse_move(drag_position)\n\t\tsimulate_mouse_button_release(MOUSE_BUTTON_LEFT)\n\tvar touch_drop_event := InputEventScreenTouch.new()\n\ttouch_drop_event.window_id = event.window_id\n\ttouch_drop_event.index = event.index\n\ttouch_drop_event.position = drag_position\n\ttouch_drop_event.pressed = false\n\t_current_scene.get_viewport().push_input(touch_drop_event)\n\tawait _scene_tree().process_frame\n\treturn self\n\n\nfunc set_time_factor(time_factor: float = 1.0) -> GdUnitSceneRunner:\n\t_time_factor = min(9.0, time_factor)\n\t__activate_time_factor()\n\t__print(\"set time factor: %f\" % _time_factor)\n\t__print(\"set physics physics_ticks_per_second: %d\" % (_saved_iterations_per_second*_time_factor))\n\treturn self\n\n\nfunc simulate_frames(frames: int, delta_milli: int = -1) -> GdUnitSceneRunner:\n\tvar time_shift_frames :int = max(1, frames / _time_factor)\n\tfor frame in time_shift_frames:\n\t\tif delta_milli == -1:\n\t\t\tawait _scene_tree().process_frame\n\t\telse:\n\t\t\tawait _scene_tree().create_timer(delta_milli * 0.001).timeout\n\treturn self\n\n\nfunc simulate_until_signal(\n\tsignal_name: String,\n\targ0: Variant = NO_ARG,\n\targ1: Variant = NO_ARG,\n\targ2: Variant = NO_ARG,\n\targ3: Variant = NO_ARG,\n\targ4: Variant = NO_ARG,\n\targ5: Variant = NO_ARG,\n\targ6: Variant = NO_ARG,\n\targ7: Variant = NO_ARG,\n\targ8: Variant = NO_ARG,\n\targ9: Variant = NO_ARG) -> GdUnitSceneRunner:\n\tvar args: Array = GdArrayTools.filter_value([arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9], NO_ARG)\n\tawait _awaiter.await_signal_idle_frames(scene(), signal_name, args, 10000)\n\treturn self\n\n\nfunc simulate_until_object_signal(\n\tsource: Object,\n\tsignal_name: String,\n\targ0: Variant = NO_ARG,\n\targ1: Variant = NO_ARG,\n\targ2: Variant = NO_ARG,\n\targ3: Variant = NO_ARG,\n\targ4: Variant = NO_ARG,\n\targ5: Variant = NO_ARG,\n\targ6: Variant = NO_ARG,\n\targ7: Variant = NO_ARG,\n\targ8: Variant = NO_ARG,\n\targ9: Variant = NO_ARG) -> GdUnitSceneRunner:\n\tvar args: Array = GdArrayTools.filter_value([arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9], NO_ARG)\n\tawait _awaiter.await_signal_idle_frames(source, signal_name, args, 10000)\n\treturn self\n\n\nfunc await_func(func_name: String, args := []) -> GdUnitFuncAssert:\n\treturn GdUnitFuncAssertImpl.new(scene(), func_name, args)\n\n\nfunc await_func_on(instance: Object, func_name: String, args := []) -> GdUnitFuncAssert:\n\treturn GdUnitFuncAssertImpl.new(instance, func_name, args)\n\n\nfunc await_signal(signal_name: String, args := [], timeout := 2000 ) -> void:\n\tawait _awaiter.await_signal_on(scene(), signal_name, args, timeout)\n\n\nfunc await_signal_on(source: Object, signal_name: String, args := [], timeout := 2000 ) -> void:\n\tawait _awaiter.await_signal_on(source, signal_name, args, timeout)\n\n\nfunc maximize_view() -> GdUnitSceneRunner:\n\tDisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)\n\tDisplayServer.window_move_to_foreground()\n\treturn self\n\n\nfunc _property_exists(name: String) -> bool:\n\treturn scene().get_property_list().any(func(properties :Dictionary) -> bool: return properties[\"name\"] == name)\n\n\nfunc get_property(name: String) -> Variant:\n\tif not _property_exists(name):\n\t\treturn \"The property '%s' not exist checked loaded scene.\" % name\n\treturn scene().get(name)\n\n\nfunc set_property(name: String, value: Variant) -> bool:\n\tif not _property_exists(name):\n\t\tpush_error(\"The property named '%s' cannot be set, it does not exist!\" % name)\n\t\treturn false;\n\tscene().set(name, value)\n\treturn true\n\n\nfunc invoke(\n\tname: String,\n\targ0: Variant = NO_ARG,\n\targ1: Variant = NO_ARG,\n\targ2: Variant = NO_ARG,\n\targ3: Variant = NO_ARG,\n\targ4: Variant = NO_ARG,\n\targ5: Variant = NO_ARG,\n\targ6: Variant = NO_ARG,\n\targ7: Variant = NO_ARG,\n\targ8: Variant = NO_ARG,\n\targ9: Variant = NO_ARG) -> Variant:\n\tvar args: Array = GdArrayTools.filter_value([arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9], NO_ARG)\n\tif scene().has_method(name):\n\t\treturn scene().callv(name, args)\n\treturn \"The method '%s' not exist checked loaded scene.\" % name\n\n\nfunc find_child(name: String, recursive: bool = true, owned: bool = false) -> Node:\n\treturn scene().find_child(name, recursive, owned)\n\n\nfunc _scene_name() -> String:\n\tvar scene_script :GDScript = scene().get_script()\n\tvar scene_name :String = scene().get_name()\n\tif not scene_script:\n\t\treturn scene_name\n\tif not scene_name.begins_with(\"@\"):\n\t\treturn scene_name\n\treturn scene_script.resource_name.get_basename()\n\n\nfunc __activate_time_factor() -> void:\n\tEngine.set_time_scale(_time_factor)\n\tEngine.set_physics_ticks_per_second((_saved_iterations_per_second * _time_factor) as int)\n\n\nfunc __deactivate_time_factor() -> void:\n\tEngine.set_time_scale(1)\n\tEngine.set_physics_ticks_per_second(_saved_iterations_per_second as int)\n\n\n# copy over current active modifiers\nfunc _apply_input_modifiers(event: InputEvent) -> void:\n\tif _last_input_event is InputEventWithModifiers and event is InputEventWithModifiers:\n\t\tvar last_input_event := _last_input_event as InputEventWithModifiers\n\t\tvar _event := event as InputEventWithModifiers\n\t\t_event.meta_pressed = _event.meta_pressed or last_input_event.meta_pressed\n\t\t_event.alt_pressed = _event.alt_pressed or last_input_event.alt_pressed\n\t\t_event.shift_pressed = _event.shift_pressed or last_input_event.shift_pressed\n\t\t_event.ctrl_pressed = _event.ctrl_pressed or last_input_event.ctrl_pressed\n\t\t# this line results into reset the control_pressed state!!!\n\t\t#event.command_or_control_autoremap = event.command_or_control_autoremap or _last_input_event.command_or_control_autoremap\n\n\n# copy over current active mouse mask and combine with curren mask\nfunc _apply_input_mouse_mask(event: InputEvent) -> void:\n\t# first apply last mask\n\tif _last_input_event is InputEventMouse and event is InputEventMouse:\n\t\t(event as InputEventMouse).button_mask |= (_last_input_event as InputEventMouse).button_mask\n\tif event is InputEventMouseButton:\n\t\tvar _event := event as InputEventMouseButton\n\t\tvar button_mask :int = MAP_MOUSE_BUTTON_MASKS.get(_event.get_button_index(), 0)\n\t\tif _event.is_pressed():\n\t\t\t_event.button_mask |= button_mask\n\t\telse:\n\t\t\t_event.button_mask ^= button_mask\n\n\n# copy over last mouse position if need\nfunc _apply_input_mouse_position(event: InputEvent) -> void:\n\tif _last_input_event is InputEventMouse and event is InputEventMouseButton:\n\t\t(event as InputEventMouseButton).position = (_last_input_event as InputEventMouse).position\n\n\n## handle input action via Input modifieres\nfunc _handle_actions(event: InputEventAction) -> bool:\n\tif not InputMap.event_is_action(event, event.action, true):\n\t\treturn false\n\t__print(\"\tprocess action %s (%s) <- %s\" % [scene(), _scene_name(), event.as_text()])\n\tif event.is_pressed():\n\t\tInput.action_press(event.action, InputMap.action_get_deadzone(event.action))\n\telse:\n\t\tInput.action_release(event.action)\n\treturn true\n\n\n# for handling read https://docs.godotengine.org/en/stable/tutorials/inputs/inputevent.html?highlight=inputevent#how-does-it-work\n@warning_ignore(\"return_value_discarded\")\nfunc _handle_input_event(event: InputEvent) -> GdUnitSceneRunner:\n\tif event is InputEventMouse:\n\t\tInput.warp_mouse((event as InputEventMouse).position as Vector2)\n\tInput.parse_input_event(event)\n\n\tif event is InputEventAction:\n\t\t_handle_actions(event as InputEventAction)\n\n\tvar current_scene := scene()\n\tif is_instance_valid(current_scene):\n\t\t# do not flush events if node processing disabled otherwise we run into errors at tree removed\n\t\tif _current_scene.process_mode != Node.PROCESS_MODE_DISABLED:\n\t\t\tInput.flush_buffered_events()\n\t\t__print(\"\tprocess event %s (%s) <- %s\" % [current_scene, _scene_name(), event.as_text()])\n\t\tif(current_scene.has_method(\"_gui_input\")):\n\t\t\t(current_scene as Control)._gui_input(event)\n\t\tif(current_scene.has_method(\"_unhandled_input\")):\n\t\t\tcurrent_scene._unhandled_input(event)\n\t\tcurrent_scene.get_viewport().set_input_as_handled()\n\n\t# save last input event needs to be merged with next InputEventMouseButton\n\t_last_input_event = event\n\treturn self\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc _reset_input_to_default() -> void:\n\t# reset all mouse button to inital state if need\n\tfor m_button :int in _mouse_button_on_press.duplicate():\n\t\tif Input.is_mouse_button_pressed(m_button):\n\t\t\tsimulate_mouse_button_release(m_button)\n\t_mouse_button_on_press.clear()\n\n\tfor key_scancode :int in _key_on_press.duplicate():\n\t\tif Input.is_key_pressed(key_scancode):\n\t\t\tsimulate_key_release(key_scancode)\n\t_key_on_press.clear()\n\n\tfor action :String in _action_on_press.duplicate():\n\t\tif Input.is_action_pressed(action):\n\t\t\tsimulate_action_release(action)\n\t_action_on_press.clear()\n\n\tif is_instance_valid(_current_scene) and _current_scene.process_mode != Node.PROCESS_MODE_DISABLED:\n\t\tInput.flush_buffered_events()\n\t_last_input_event = null\n\n\nfunc __print(message: String) -> void:\n\tif _verbose:\n\t\tprints(message)\n\n\nfunc __print_current_focus() -> void:\n\tif not _verbose:\n\t\treturn\n\tvar focused_node := scene().get_viewport().gui_get_focus_owner()\n\tif focused_node:\n\t\tprints(\"\tfocus checked %s\" % focused_node)\n\telse:\n\t\tprints(\"\tno focus set\")\n\n\nfunc scene() -> Node:\n\tif is_instance_valid(_current_scene):\n\t\treturn _current_scene\n\tif not _is_disposed:\n\t\tpush_error(\"The current scene instance is not valid anymore! check your test is valid. e.g. check for missing awaits.\")\n\treturn null\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd.uid",
    "content": "uid://ba3nuheatpt6y\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitScriptType.gd",
    "content": "class_name GdUnitScriptType\nextends RefCounted\n\nconst UNKNOWN := \"\"\nconst CS := \"cs\"\nconst GD := \"gd\"\n\n\nstatic func type_of(script :Script) -> String:\n\tif script == null:\n\t\treturn UNKNOWN\n\tif GdObjects.is_gd_script(script):\n\t\treturn GD\n\tif GdObjects.is_cs_script(script):\n\t\treturn CS\n\treturn UNKNOWN\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitScriptType.gd.uid",
    "content": "uid://clunbra827ekj\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitSettings.gd",
    "content": "@tool\nclass_name GdUnitSettings\nextends RefCounted\n\n\nconst MAIN_CATEGORY = \"gdunit4\"\n# Common Settings\nconst COMMON_SETTINGS = MAIN_CATEGORY + \"/settings\"\n\nconst GROUP_COMMON = COMMON_SETTINGS + \"/common\"\nconst UPDATE_NOTIFICATION_ENABLED = GROUP_COMMON + \"/update_notification_enabled\"\nconst SERVER_TIMEOUT = GROUP_COMMON + \"/server_connection_timeout_minutes\"\n\nconst GROUP_TEST = COMMON_SETTINGS + \"/test\"\nconst TEST_TIMEOUT = GROUP_TEST + \"/test_timeout_seconds\"\nconst TEST_LOOKUP_FOLDER = GROUP_TEST + \"/test_lookup_folder\"\nconst TEST_SUITE_NAMING_CONVENTION = GROUP_TEST + \"/test_suite_naming_convention\"\nconst TEST_DISCOVER_ENABLED = GROUP_TEST + \"/test_discovery\"\nconst TEST_FLAKY_CHECK = GROUP_TEST + \"/flaky_check_enable\"\nconst TEST_FLAKY_MAX_RETRIES = GROUP_TEST + \"/flaky_max_retries\"\n\n\n# Report Setiings\nconst REPORT_SETTINGS = MAIN_CATEGORY + \"/report\"\nconst GROUP_GODOT = REPORT_SETTINGS + \"/godot\"\nconst REPORT_PUSH_ERRORS = GROUP_GODOT + \"/push_error\"\nconst REPORT_SCRIPT_ERRORS = GROUP_GODOT + \"/script_error\"\nconst REPORT_ORPHANS  = REPORT_SETTINGS + \"/verbose_orphans\"\nconst GROUP_ASSERT = REPORT_SETTINGS + \"/assert\"\nconst REPORT_ASSERT_WARNINGS = GROUP_ASSERT + \"/verbose_warnings\"\nconst REPORT_ASSERT_ERRORS   = GROUP_ASSERT + \"/verbose_errors\"\nconst REPORT_ASSERT_STRICT_NUMBER_TYPE_COMPARE = GROUP_ASSERT + \"/strict_number_type_compare\"\n\n# Godot debug stdout/logging settings\nconst CATEGORY_LOGGING := \"debug/file_logging/\"\nconst STDOUT_ENABLE_TO_FILE = CATEGORY_LOGGING + \"enable_file_logging\"\nconst STDOUT_WITE_TO_FILE = CATEGORY_LOGGING + \"log_path\"\n\n\n# GdUnit Templates\nconst TEMPLATES = MAIN_CATEGORY + \"/templates\"\nconst TEMPLATES_TS = TEMPLATES + \"/testsuite\"\nconst TEMPLATE_TS_GD = TEMPLATES_TS + \"/GDScript\"\nconst TEMPLATE_TS_CS = TEMPLATES_TS + \"/CSharpScript\"\n\n\n# UI Setiings\nconst UI_SETTINGS = MAIN_CATEGORY + \"/ui\"\nconst GROUP_UI_INSPECTOR = UI_SETTINGS + \"/inspector\"\nconst INSPECTOR_NODE_COLLAPSE = GROUP_UI_INSPECTOR + \"/node_collapse\"\nconst INSPECTOR_TREE_VIEW_MODE = GROUP_UI_INSPECTOR + \"/tree_view_mode\"\nconst INSPECTOR_TREE_SORT_MODE = GROUP_UI_INSPECTOR + \"/tree_sort_mode\"\n\n\n# Shortcut Setiings\nconst SHORTCUT_SETTINGS = MAIN_CATEGORY + \"/Shortcuts\"\nconst GROUP_SHORTCUT_INSPECTOR = SHORTCUT_SETTINGS + \"/inspector\"\nconst SHORTCUT_INSPECTOR_RERUN_TEST = GROUP_SHORTCUT_INSPECTOR + \"/rerun_test\"\nconst SHORTCUT_INSPECTOR_RERUN_TEST_DEBUG = GROUP_SHORTCUT_INSPECTOR + \"/rerun_test_debug\"\nconst SHORTCUT_INSPECTOR_RUN_TEST_OVERALL = GROUP_SHORTCUT_INSPECTOR + \"/run_test_overall\"\nconst SHORTCUT_INSPECTOR_RUN_TEST_STOP = GROUP_SHORTCUT_INSPECTOR + \"/run_test_stop\"\n\nconst GROUP_SHORTCUT_EDITOR = SHORTCUT_SETTINGS + \"/editor\"\nconst SHORTCUT_EDITOR_RUN_TEST = GROUP_SHORTCUT_EDITOR + \"/run_test\"\nconst SHORTCUT_EDITOR_RUN_TEST_DEBUG = GROUP_SHORTCUT_EDITOR + \"/run_test_debug\"\nconst SHORTCUT_EDITOR_CREATE_TEST = GROUP_SHORTCUT_EDITOR + \"/create_test\"\n\nconst GROUP_SHORTCUT_FILESYSTEM = SHORTCUT_SETTINGS + \"/filesystem\"\nconst SHORTCUT_FILESYSTEM_RUN_TEST = GROUP_SHORTCUT_FILESYSTEM + \"/run_test\"\nconst SHORTCUT_FILESYSTEM_RUN_TEST_DEBUG = GROUP_SHORTCUT_FILESYSTEM + \"/run_test_debug\"\n\n\n# Toolbar Setiings\nconst GROUP_UI_TOOLBAR = UI_SETTINGS + \"/toolbar\"\nconst INSPECTOR_TOOLBAR_BUTTON_RUN_OVERALL = GROUP_UI_TOOLBAR + \"/run_overall\"\n\n# defaults\n# server connection timeout in minutes\nconst DEFAULT_SERVER_TIMEOUT :int = 30\n# test case runtime timeout in seconds\nconst DEFAULT_TEST_TIMEOUT :int = 60*5\n# the folder to create new test-suites\nconst DEFAULT_TEST_LOOKUP_FOLDER := \"test\"\n\n# help texts\nconst HELP_TEST_LOOKUP_FOLDER := \"Subfolder where test suites are located (or empty to use source folder directly)\"\n\nenum NAMING_CONVENTIONS {\n\tAUTO_DETECT,\n\tSNAKE_CASE,\n\tPASCAL_CASE,\n}\n\n\nconst _VALUE_SET_SEPARATOR = \"\\f\" # ASCII Form-feed character (AKA page break)\n\n\nstatic func setup() -> void:\n\tcreate_property_if_need(UPDATE_NOTIFICATION_ENABLED, true, \"Show notification if new gdUnit4 version is found\")\n\t# test settings\n\tcreate_property_if_need(SERVER_TIMEOUT, DEFAULT_SERVER_TIMEOUT, \"Server connection timeout in minutes\")\n\tcreate_property_if_need(TEST_TIMEOUT, DEFAULT_TEST_TIMEOUT, \"Test case runtime timeout in seconds\")\n\tcreate_property_if_need(TEST_LOOKUP_FOLDER, DEFAULT_TEST_LOOKUP_FOLDER, HELP_TEST_LOOKUP_FOLDER)\n\tcreate_property_if_need(TEST_SUITE_NAMING_CONVENTION, NAMING_CONVENTIONS.AUTO_DETECT, \"Naming convention to use when generating testsuites\", NAMING_CONVENTIONS.keys())\n\tcreate_property_if_need(TEST_DISCOVER_ENABLED, false, \"Automatically detect new tests in test lookup folders at runtime\")\n\tcreate_property_if_need(TEST_FLAKY_CHECK, false, \"Rerun tests on failure and mark them as FLAKY\")\n\tcreate_property_if_need(TEST_FLAKY_MAX_RETRIES, 3, \"Sets the number of retries for rerunning a flaky test\")\n\t# report settings\n\tcreate_property_if_need(REPORT_PUSH_ERRORS, false, \"Report push_error() as failure\")\n\tcreate_property_if_need(REPORT_SCRIPT_ERRORS, true, \"Report script errors as failure\")\n\tcreate_property_if_need(REPORT_ORPHANS, true, \"Report orphaned nodes after tests finish\")\n\tcreate_property_if_need(REPORT_ASSERT_ERRORS, true, \"Report assertion failures as errors\")\n\tcreate_property_if_need(REPORT_ASSERT_WARNINGS, true, \"Report assertion failures as warnings\")\n\tcreate_property_if_need(REPORT_ASSERT_STRICT_NUMBER_TYPE_COMPARE, true, \"Compare number values strictly by type (real vs int)\")\n\t# inspector\n\tcreate_property_if_need(INSPECTOR_NODE_COLLAPSE, true,\n\t\t\"Close testsuite node after a successful test run.\")\n\tcreate_property_if_need(INSPECTOR_TREE_VIEW_MODE, GdUnitInspectorTreeConstants.TREE_VIEW_MODE.TREE,\n\t\t\"Inspector panel presentation mode\", GdUnitInspectorTreeConstants.TREE_VIEW_MODE.keys())\n\tcreate_property_if_need(INSPECTOR_TREE_SORT_MODE, GdUnitInspectorTreeConstants.SORT_MODE.UNSORTED,\n\t\t\"Inspector panel sorting mode\", GdUnitInspectorTreeConstants.SORT_MODE.keys())\n\tcreate_property_if_need(INSPECTOR_TOOLBAR_BUTTON_RUN_OVERALL, false,\n\t\t\"Show 'Run overall Tests' button in the inspector toolbar\")\n\tcreate_property_if_need(TEMPLATE_TS_GD, GdUnitTestSuiteTemplate.default_GD_template(), \"Test suite template to use\")\n\tcreate_shortcut_properties_if_need()\n\tmigrate_properties()\n\n\nstatic func migrate_properties() -> void:\n\tvar TEST_ROOT_FOLDER := \"gdunit4/settings/test/test_root_folder\"\n\tif get_property(TEST_ROOT_FOLDER) != null:\n\t\tmigrate_property(TEST_ROOT_FOLDER,\\\n\t\t\tTEST_LOOKUP_FOLDER,\\\n\t\t\tDEFAULT_TEST_LOOKUP_FOLDER,\\\n\t\t\tHELP_TEST_LOOKUP_FOLDER,\\\n\t\t\tfunc(value :Variant) -> String: return DEFAULT_TEST_LOOKUP_FOLDER if value == null else value)\n\n\nstatic func create_shortcut_properties_if_need() -> void:\n\t# inspector\n\tcreate_property_if_need(SHORTCUT_INSPECTOR_RERUN_TEST, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.RERUN_TESTS), \"Rerun the most recently executed tests\")\n\tcreate_property_if_need(SHORTCUT_INSPECTOR_RERUN_TEST_DEBUG, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.RERUN_TESTS_DEBUG), \"Rerun the most recently executed tests (Debug mode)\")\n\tcreate_property_if_need(SHORTCUT_INSPECTOR_RUN_TEST_OVERALL, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.RUN_TESTS_OVERALL), \"Runs all tests (Debug mode)\")\n\tcreate_property_if_need(SHORTCUT_INSPECTOR_RUN_TEST_STOP, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.STOP_TEST_RUN), \"Stop the current test execution\")\n\t# script editor\n\tcreate_property_if_need(SHORTCUT_EDITOR_RUN_TEST, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.RUN_TESTCASE), \"Run the currently selected test\")\n\tcreate_property_if_need(SHORTCUT_EDITOR_RUN_TEST_DEBUG, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.RUN_TESTCASE_DEBUG), \"Run the currently selected test (Debug mode).\")\n\tcreate_property_if_need(SHORTCUT_EDITOR_CREATE_TEST, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.CREATE_TEST), \"Create a new test case for the currently selected function\")\n\t# filesystem\n\tcreate_property_if_need(SHORTCUT_FILESYSTEM_RUN_TEST, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.NONE), \"Run all test suites in the selected folder or file\")\n\tcreate_property_if_need(SHORTCUT_FILESYSTEM_RUN_TEST_DEBUG, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.NONE), \"Run all test suites in the selected folder or file (Debug)\")\n\n\nstatic func create_property_if_need(name :String, default :Variant, help :=\"\", value_set := PackedStringArray()) -> void:\n\tif not ProjectSettings.has_setting(name):\n\t\t#prints(\"GdUnit4: Set inital settings '%s' to '%s'.\" % [name, str(default)])\n\t\tProjectSettings.set_setting(name, default)\n\n\tProjectSettings.set_initial_value(name, default)\n\thelp = help if value_set.is_empty() else \"%s%s%s\" % [help, _VALUE_SET_SEPARATOR, value_set]\n\tset_help(name, default, help)\n\n\nstatic func set_help(property_name :String, value :Variant, help :String) -> void:\n\tProjectSettings.add_property_info({\n\t\t\"name\": property_name,\n\t\t\"type\": typeof(value),\n\t\t\"hint\": PROPERTY_HINT_TYPE_STRING,\n\t\t\"hint_string\": help\n\t})\n\n\nstatic func get_setting(name :String, default :Variant) -> Variant:\n\tif ProjectSettings.has_setting(name):\n\t\treturn ProjectSettings.get_setting(name)\n\treturn default\n\n\nstatic func is_update_notification_enabled() -> bool:\n\tif ProjectSettings.has_setting(UPDATE_NOTIFICATION_ENABLED):\n\t\treturn ProjectSettings.get_setting(UPDATE_NOTIFICATION_ENABLED)\n\treturn false\n\n\nstatic func set_update_notification(enable :bool) -> void:\n\tProjectSettings.set_setting(UPDATE_NOTIFICATION_ENABLED, enable)\n\t@warning_ignore(\"return_value_discarded\")\n\tProjectSettings.save()\n\n\nstatic func get_log_path() -> String:\n\treturn ProjectSettings.get_setting(STDOUT_WITE_TO_FILE)\n\n\nstatic func set_log_path(path :String) -> void:\n\tProjectSettings.set_setting(STDOUT_ENABLE_TO_FILE, true)\n\tProjectSettings.set_setting(STDOUT_WITE_TO_FILE, path)\n\t@warning_ignore(\"return_value_discarded\")\n\tProjectSettings.save()\n\n\nstatic func set_inspector_tree_sort_mode(sort_mode: GdUnitInspectorTreeConstants.SORT_MODE) -> void:\n\tvar property := get_property(INSPECTOR_TREE_SORT_MODE)\n\tproperty.set_value(sort_mode)\n\tupdate_property(property)\n\n\nstatic func get_inspector_tree_sort_mode() -> GdUnitInspectorTreeConstants.SORT_MODE:\n\tvar property := get_property(INSPECTOR_TREE_SORT_MODE)\n\treturn property.value() if property != null else GdUnitInspectorTreeConstants.SORT_MODE.UNSORTED\n\n\nstatic func set_inspector_tree_view_mode(tree_view_mode: GdUnitInspectorTreeConstants.TREE_VIEW_MODE) -> void:\n\tvar property := get_property(INSPECTOR_TREE_VIEW_MODE)\n\tproperty.set_value(tree_view_mode)\n\tupdate_property(property)\n\n\nstatic func get_inspector_tree_view_mode() -> GdUnitInspectorTreeConstants.TREE_VIEW_MODE:\n\tvar property := get_property(INSPECTOR_TREE_VIEW_MODE)\n\treturn property.value() if property != null else GdUnitInspectorTreeConstants.TREE_VIEW_MODE.TREE\n\n\n# the configured server connection timeout in ms\nstatic func server_timeout() -> int:\n\treturn get_setting(SERVER_TIMEOUT, DEFAULT_SERVER_TIMEOUT) * 60 * 1000\n\n\n# the configured test case timeout in ms\nstatic func test_timeout() -> int:\n\treturn get_setting(TEST_TIMEOUT, DEFAULT_TEST_TIMEOUT) * 1000\n\n\n# the root folder to store/generate test-suites\nstatic func test_root_folder() -> String:\n\treturn get_setting(TEST_LOOKUP_FOLDER, DEFAULT_TEST_LOOKUP_FOLDER)\n\n\nstatic func is_verbose_assert_warnings() -> bool:\n\treturn get_setting(REPORT_ASSERT_WARNINGS, true)\n\n\nstatic func is_verbose_assert_errors() -> bool:\n\treturn get_setting(REPORT_ASSERT_ERRORS, true)\n\n\nstatic func is_verbose_orphans() -> bool:\n\treturn get_setting(REPORT_ORPHANS, true)\n\n\nstatic func is_strict_number_type_compare() -> bool:\n\treturn get_setting(REPORT_ASSERT_STRICT_NUMBER_TYPE_COMPARE, true)\n\n\nstatic func is_report_push_errors() -> bool:\n\treturn get_setting(REPORT_PUSH_ERRORS, false)\n\n\nstatic func is_report_script_errors() -> bool:\n\treturn get_setting(REPORT_SCRIPT_ERRORS, true)\n\n\nstatic func is_inspector_node_collapse() -> bool:\n\treturn get_setting(INSPECTOR_NODE_COLLAPSE, true)\n\n\nstatic func is_inspector_toolbar_button_show() -> bool:\n\treturn get_setting(INSPECTOR_TOOLBAR_BUTTON_RUN_OVERALL, true)\n\n\nstatic func is_test_discover_enabled() -> bool:\n\treturn get_setting(TEST_DISCOVER_ENABLED, false)\n\n\nstatic func is_test_flaky_check_enabled() -> bool:\n\treturn get_setting(TEST_FLAKY_CHECK, false)\n\n\nstatic func get_flaky_max_retries() -> int:\n\treturn get_setting(TEST_FLAKY_MAX_RETRIES, 3)\n\n\nstatic func set_test_discover_enabled(enable :bool) -> void:\n\tvar property := get_property(TEST_DISCOVER_ENABLED)\n\tproperty.set_value(enable)\n\tupdate_property(property)\n\n\nstatic func is_log_enabled() -> bool:\n\treturn ProjectSettings.get_setting(STDOUT_ENABLE_TO_FILE)\n\n\nstatic func list_settings(category: String) -> Array[GdUnitProperty]:\n\tvar settings: Array[GdUnitProperty] = []\n\tfor property in ProjectSettings.get_property_list():\n\t\tvar property_name :String = property[\"name\"]\n\t\tif property_name.begins_with(category):\n\t\t\tsettings.append(build_property(property_name, property))\n\treturn settings\n\n\nstatic func extract_value_set_from_help(value :String) -> PackedStringArray:\n\tvar split_value := value.split(_VALUE_SET_SEPARATOR)\n\tif not split_value.size() > 1:\n\t\treturn PackedStringArray()\n\n\tvar regex := RegEx.new()\n\t@warning_ignore(\"return_value_discarded\")\n\tregex.compile(\"\\\\[(.+)\\\\]\")\n\tvar matches := regex.search_all(split_value[1])\n\tif matches.is_empty():\n\t\treturn PackedStringArray()\n\tvar values: String = matches[0].get_string(1)\n\treturn values.replacen(\" \", \"\").replacen(\"\\\"\", \"\").split(\",\", false)\n\n\nstatic func extract_help_text(value :String) -> String:\n\treturn value.split(_VALUE_SET_SEPARATOR)[0]\n\n\nstatic func update_property(property :GdUnitProperty) -> Variant:\n\tvar current_value :Variant = ProjectSettings.get_setting(property.name())\n\tif current_value != property.value():\n\t\tvar error :Variant = validate_property_value(property)\n\t\tif error != null:\n\t\t\treturn error\n\t\tProjectSettings.set_setting(property.name(), property.value())\n\t\tGdUnitSignals.instance().gdunit_settings_changed.emit(property)\n\t\t_save_settings()\n\treturn null\n\n\nstatic func reset_property(property :GdUnitProperty) -> void:\n\tProjectSettings.set_setting(property.name(), property.default())\n\tGdUnitSignals.instance().gdunit_settings_changed.emit(property)\n\t_save_settings()\n\n\nstatic func validate_property_value(property :GdUnitProperty) -> Variant:\n\tmatch property.name():\n\t\tTEST_LOOKUP_FOLDER:\n\t\t\treturn validate_lookup_folder(property.value_as_string())\n\t\t_: return null\n\n\nstatic func validate_lookup_folder(value :String) -> Variant:\n\tif value.is_empty() or value == \"/\":\n\t\treturn null\n\tif value.contains(\"res:\"):\n\t\treturn \"Test Lookup Folder: do not allowed to contains 'res://'\"\n\tif not value.is_valid_filename():\n\t\treturn \"Test Lookup Folder: contains invalid characters! e.g (: / \\\\ ? * \\\" | % < >)\"\n\treturn null\n\n\nstatic func save_property(name :String, value :Variant) -> void:\n\tProjectSettings.set_setting(name, value)\n\t_save_settings()\n\n\nstatic func _save_settings() -> void:\n\tvar err := ProjectSettings.save()\n\tif err != OK:\n\t\tpush_error(\"Save GdUnit4 settings failed : %s\" % error_string(err))\n\t\treturn\n\n\nstatic func has_property(name :String) -> bool:\n\treturn ProjectSettings.get_property_list().any(func(property :Dictionary) -> bool: return property[\"name\"] == name)\n\n\nstatic func get_property(name :String) -> GdUnitProperty:\n\tfor property in ProjectSettings.get_property_list():\n\t\tvar property_name :String = property[\"name\"]\n\t\tif property_name == name:\n\t\t\treturn build_property(name, property)\n\treturn null\n\n\nstatic func build_property(property_name: String, property: Dictionary) -> GdUnitProperty:\n\tvar value: Variant = ProjectSettings.get_setting(property_name)\n\tvar value_type: int = property[\"type\"]\n\tvar default: Variant = ProjectSettings.property_get_revert(property_name)\n\tvar help: String = property[\"hint_string\"]\n\tvar value_set := extract_value_set_from_help(help)\n\treturn GdUnitProperty.new(property_name, value_type, value, default, extract_help_text(help), value_set)\n\n\nstatic func migrate_property(old_property :String, new_property :String, default_value :Variant, help :String, converter := Callable()) -> void:\n\tvar property := get_property(old_property)\n\tif property == null:\n\t\tprints(\"Migration not possible, property '%s' not found\" % old_property)\n\t\treturn\n\tvar value :Variant = converter.call(property.value()) if converter.is_valid() else property.value()\n\tProjectSettings.set_setting(new_property, value)\n\tProjectSettings.set_initial_value(new_property, default_value)\n\tset_help(new_property, value, help)\n\tProjectSettings.clear(old_property)\n\tprints(\"Successfully migrated property '%s' -> '%s' value: %s\" % [old_property, new_property, value])\n\n\nstatic func dump_to_tmp() -> void:\n\t@warning_ignore(\"return_value_discarded\")\n\tProjectSettings.save_custom(\"user://project_settings.godot\")\n\n\nstatic func restore_dump_from_tmp() -> void:\n\t@warning_ignore(\"return_value_discarded\")\n\tDirAccess.copy_absolute(\"user://project_settings.godot\", \"res://project.godot\")\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitSettings.gd.uid",
    "content": "uid://b2d7jj4pvo14x\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitSignalAwaiter.gd",
    "content": "class_name GdUnitSignalAwaiter\nextends RefCounted\n\nsignal signal_emitted(action :Variant)\n\nconst NO_ARG :Variant = GdUnitConstants.NO_ARG\n\nvar _wait_on_idle_frame := false\nvar _interrupted := false\nvar _time_left :float = 0\nvar _timeout_millis :int\n\n\nfunc _init(timeout_millis :int, wait_on_idle_frame := false) -> void:\n\t_timeout_millis = timeout_millis\n\t_wait_on_idle_frame = wait_on_idle_frame\n\n\nfunc _on_signal_emmited(\n\targ0 :Variant = NO_ARG,\n\targ1 :Variant = NO_ARG,\n\targ2 :Variant = NO_ARG,\n\targ3 :Variant = NO_ARG,\n\targ4 :Variant = NO_ARG,\n\targ5 :Variant = NO_ARG,\n\targ6 :Variant = NO_ARG,\n\targ7 :Variant = NO_ARG,\n\targ8 :Variant = NO_ARG,\n\targ9 :Variant = NO_ARG) -> void:\n\tvar signal_args :Variant = GdArrayTools.filter_value([arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9], NO_ARG)\n\tsignal_emitted.emit(signal_args)\n\n\nfunc is_interrupted() -> bool:\n\treturn _interrupted\n\n\nfunc elapsed_time() -> float:\n\treturn _time_left\n\n\nfunc on_signal(source :Object, signal_name :String, expected_signal_args :Array) -> Variant:\n\t# register checked signal to wait for\n\t@warning_ignore(\"return_value_discarded\")\n\tsource.connect(signal_name, _on_signal_emmited)\n\t# install timeout timer\n\tvar scene_tree := Engine.get_main_loop() as SceneTree\n\tvar timer := Timer.new()\n\tscene_tree.root.add_child(timer)\n\ttimer.add_to_group(\"GdUnitTimers\")\n\ttimer.set_one_shot(true)\n\t@warning_ignore(\"return_value_discarded\")\n\ttimer.timeout.connect(_do_interrupt, CONNECT_DEFERRED)\n\ttimer.start(_timeout_millis * 0.001 * Engine.get_time_scale())\n\n\t# holds the emited value\n\tvar value :Variant\n\t# wait for signal is emitted or a timeout is happen\n\twhile true:\n\t\tvalue = await signal_emitted\n\t\tif _interrupted:\n\t\t\tbreak\n\t\tif not (value is Array):\n\t\t\tvalue = [value]\n\t\tif expected_signal_args.size() == 0 or GdObjects.equals(value, expected_signal_args):\n\t\t\tbreak\n\t\tawait scene_tree.process_frame\n\n\tsource.disconnect(signal_name, _on_signal_emmited)\n\t_time_left = timer.time_left\n\tawait scene_tree.process_frame\n\t@warning_ignore(\"unsafe_cast\")\n\tif value is Array and (value as Array).size() == 1:\n\t\treturn value[0]\n\treturn value\n\n\nfunc _do_interrupt() -> void:\n\t_interrupted = true\n\tsignal_emitted.emit(null)\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitSignalAwaiter.gd.uid",
    "content": "uid://c65sgy1avbnbl\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitSignalCollector.gd",
    "content": "# It connects to all signals of given emitter and collects received signals and arguments\n# The collected signals are cleand finally when the emitter is freed.\nclass_name GdUnitSignalCollector\nextends RefCounted\n\nconst NO_ARG :Variant = GdUnitConstants.NO_ARG\nconst SIGNAL_BLACK_LIST = []#[\"tree_exiting\", \"tree_exited\", \"child_exiting_tree\"]\n\n# {\n#\temitter<Object> : {\n#\t\tsignal_name<String> : [signal_args<Array>],\n#\t\t...\n#\t}\n# }\nvar _collected_signals :Dictionary = {}\n\n\nfunc clear() -> void:\n\tfor emitter :Object in _collected_signals.keys():\n\t\tif is_instance_valid(emitter):\n\t\t\tunregister_emitter(emitter)\n\n\n# connect to all possible signals defined by the emitter\n# prepares the signal collection to store received signals and arguments\nfunc register_emitter(emitter :Object) -> void:\n\tif is_instance_valid(emitter):\n\t\t# check emitter is already registerd\n\t\tif _collected_signals.has(emitter):\n\t\t\treturn\n\t\t_collected_signals[emitter] = Dictionary()\n\t\t# connect to 'tree_exiting' of the emitter to finally release all acquired resources/connections.\n\t\tif emitter is Node and !(emitter as Node).tree_exiting.is_connected(unregister_emitter):\n\t\t\t(emitter as Node).tree_exiting.connect(unregister_emitter.bind(emitter))\n\t\t# connect to all signals of the emitter we want to collect\n\t\tfor signal_def in emitter.get_signal_list():\n\t\t\tvar signal_name :String = signal_def[\"name\"]\n\t\t\t# set inital collected to empty\n\t\t\tif not is_signal_collecting(emitter, signal_name):\n\t\t\t\t_collected_signals[emitter][signal_name] = Array()\n\t\t\tif SIGNAL_BLACK_LIST.find(signal_name) != -1:\n\t\t\t\tcontinue\n\t\t\tif !emitter.is_connected(signal_name, _on_signal_emmited):\n\t\t\t\tvar err := emitter.connect(signal_name, _on_signal_emmited.bind(emitter, signal_name))\n\t\t\t\tif err != OK:\n\t\t\t\t\tpush_error(\"Can't connect to signal %s on %s. Error: %s\" % [signal_name, emitter, error_string(err)])\n\n\n# unregister all acquired resources/connections, otherwise it ends up in orphans\n# is called when the emitter is removed from the parent\nfunc unregister_emitter(emitter :Object) -> void:\n\tif is_instance_valid(emitter):\n\t\tfor signal_def in emitter.get_signal_list():\n\t\t\tvar signal_name :String = signal_def[\"name\"]\n\t\t\tif emitter.is_connected(signal_name, _on_signal_emmited):\n\t\t\t\temitter.disconnect(signal_name, _on_signal_emmited.bind(emitter, signal_name))\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\t_collected_signals.erase(emitter)\n\n\n# receives the signal from the emitter with all emitted signal arguments and additional the emitter and signal_name as last two arguements\nfunc _on_signal_emmited(\n\targ0 :Variant= NO_ARG,\n\targ1 :Variant= NO_ARG,\n\targ2 :Variant= NO_ARG,\n\targ3 :Variant= NO_ARG,\n\targ4 :Variant= NO_ARG,\n\targ5 :Variant= NO_ARG,\n\targ6 :Variant= NO_ARG,\n\targ7 :Variant= NO_ARG,\n\targ8 :Variant= NO_ARG,\n\targ9 :Variant= NO_ARG,\n\targ10 :Variant= NO_ARG,\n\targ11 :Variant= NO_ARG) -> void:\n\tvar signal_args :Array = GdArrayTools.filter_value([arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11], NO_ARG)\n\t# extract the emitter and signal_name from the last two arguments (see line 61 where is added)\n\tvar signal_name :String = signal_args.pop_back()\n\tvar emitter :Object = signal_args.pop_back()\n\t#prints(\"_on_signal_emmited:\", emitter, signal_name, signal_args)\n\tif is_signal_collecting(emitter, signal_name):\n\t\t@warning_ignore(\"unsafe_cast\")\n\t\t(_collected_signals[emitter][signal_name] as Array).append(signal_args)\n\n\nfunc reset_received_signals(emitter :Object, signal_name: String, signal_args :Array) -> void:\n\t#_debug_signal_list(\"before claer\");\n\tif _collected_signals.has(emitter):\n\t\tvar signals_by_emitter :Dictionary = _collected_signals[emitter]\n\t\tif signals_by_emitter.has(signal_name):\n\t\t\t@warning_ignore(\"unsafe_cast\")\n\t\t\t(_collected_signals[emitter][signal_name] as Array).erase(signal_args)\n\t#_debug_signal_list(\"after claer\");\n\n\nfunc is_signal_collecting(emitter :Object, signal_name :String) -> bool:\n\t@warning_ignore(\"unsafe_cast\")\n\treturn _collected_signals.has(emitter) and (_collected_signals[emitter] as Dictionary).has(signal_name)\n\n\nfunc match(emitter :Object, signal_name :String, args :Array) -> bool:\n\t#prints(\"match\", signal_name,  _collected_signals[emitter][signal_name]);\n\tif _collected_signals.is_empty() or not _collected_signals.has(emitter):\n\t\treturn false\n\tfor received_args :Variant in _collected_signals[emitter][signal_name]:\n\t\t#prints(\"testing\", signal_name, received_args, \"vs\", args)\n\t\tif GdObjects.equals(received_args, args):\n\t\t\treturn true\n\treturn false\n\n\nfunc _debug_signal_list(message :String) -> void:\n\tprints(\"-----\", message, \"-------\")\n\tprints(\"senders {\")\n\tfor emitter :Object in _collected_signals:\n\t\tprints(\"\\t\", emitter)\n\t\tfor signal_name :String in _collected_signals[emitter]:\n\t\t\tvar args :Variant = _collected_signals[emitter][signal_name]\n\t\t\tprints(\"\\t\\t\", signal_name, args)\n\tprints(\"}\")\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitSignalCollector.gd.uid",
    "content": "uid://xgc30hv7dy8h\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitSignals.gd",
    "content": "class_name GdUnitSignals\nextends RefCounted\n\n@warning_ignore(\"unused_signal\")\nsignal gdunit_client_connected(client_id :int)\n@warning_ignore(\"unused_signal\")\nsignal gdunit_client_disconnected(client_id :int)\n@warning_ignore(\"unused_signal\")\nsignal gdunit_client_terminated()\n\n@warning_ignore(\"unused_signal\")\nsignal gdunit_event(event :GdUnitEvent)\n@warning_ignore(\"unused_signal\")\nsignal gdunit_event_debug(event :GdUnitEvent)\n@warning_ignore(\"unused_signal\")\nsignal gdunit_add_test_suite(test_suite :GdUnitTestSuiteDto)\n@warning_ignore(\"unused_signal\")\nsignal gdunit_message(message :String)\n@warning_ignore(\"unused_signal\")\nsignal gdunit_set_test_failed(is_failed :bool)\n@warning_ignore(\"unused_signal\")\nsignal gdunit_settings_changed(property :GdUnitProperty)\n\nconst META_KEY := \"GdUnitSignals\"\n\n\nstatic func instance() -> GdUnitSignals:\n\tif Engine.has_meta(META_KEY):\n\t\treturn Engine.get_meta(META_KEY)\n\tvar instance_ := GdUnitSignals.new()\n\tEngine.set_meta(META_KEY, instance_)\n\treturn instance_\n\n\nstatic func dispose() -> void:\n\tvar signals := instance()\n\t# cleanup connected signals\n\tfor signal_ in signals.get_signal_list():\n\t\t@warning_ignore(\"unsafe_cast\")\n\t\tfor connection in signals.get_signal_connection_list(signal_[\"name\"] as StringName):\n\t\t\tvar _signal: Signal = connection[\"signal\"]\n\t\t\tvar _callable: Callable = connection[\"callable\"]\n\t\t\t_signal.disconnect(_callable)\n\tsignals = null\n\tEngine.remove_meta(META_KEY)\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitSignals.gd.uid",
    "content": "uid://dwtotu432whd8\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitSingleton.gd",
    "content": "################################################################################\n# Provides access to a global accessible singleton\n#\n# This is a workarount to the existing auto load singleton because of some bugs\n# around plugin handling\n################################################################################\nclass_name GdUnitSingleton\nextends Object\n\n\nconst GdUnitTools := preload(\"res://addons/gdUnit4/src/core/GdUnitTools.gd\")\nconst MEATA_KEY := \"GdUnitSingletons\"\n\n\nstatic func instance(name :String, clazz :Callable) -> Variant:\n\tif Engine.has_meta(name):\n\t\treturn Engine.get_meta(name)\n\tvar singleton :Variant = clazz.call()\n\tif  is_instance_of(singleton, RefCounted):\n\t\t@warning_ignore(\"unsafe_cast\")\n\t\tpush_error(\"Invalid singleton implementation detected for '%s' is `%s`!\" % [name, (singleton as RefCounted).get_class()])\n\t\treturn\n\n\tEngine.set_meta(name, singleton)\n\tGdUnitTools.prints_verbose(\"Register singleton '%s:%s'\" % [name, singleton])\n\tvar singletons :PackedStringArray = Engine.get_meta(MEATA_KEY, PackedStringArray())\n\t@warning_ignore(\"return_value_discarded\")\n\tsingletons.append(name)\n\tEngine.set_meta(MEATA_KEY, singletons)\n\treturn singleton\n\n\nstatic func unregister(p_singleton :String, use_call_deferred :bool = false) -> void:\n\tvar singletons :PackedStringArray = Engine.get_meta(MEATA_KEY, PackedStringArray())\n\tif singletons.has(p_singleton):\n\t\tGdUnitTools.prints_verbose(\"\\n\tUnregister singleton '%s'\" % p_singleton);\n\t\tvar index := singletons.find(p_singleton)\n\t\tsingletons.remove_at(index)\n\t\tvar instance_ :Object = Engine.get_meta(p_singleton)\n\t\tGdUnitTools.prints_verbose(\"\tFree singleton instance '%s:%s'\" % [p_singleton, instance_])\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tGdUnitTools.free_instance(instance_, use_call_deferred)\n\t\tEngine.remove_meta(p_singleton)\n\t\tGdUnitTools.prints_verbose(\"\tSuccessfully freed '%s'\" % p_singleton)\n\tEngine.set_meta(MEATA_KEY, singletons)\n\n\nstatic func dispose(use_call_deferred :bool = false) -> void:\n\t# use a copy because unregister is modify the singletons array\n\tvar singletons :PackedStringArray = Engine.get_meta(MEATA_KEY, PackedStringArray())\n\tGdUnitTools.prints_verbose(\"----------------------------------------------------------------\")\n\tGdUnitTools.prints_verbose(\"Cleanup singletons %s\" % singletons)\n\tfor singleton in PackedStringArray(singletons):\n\t\tunregister(singleton, use_call_deferred)\n\tEngine.remove_meta(MEATA_KEY)\n\tGdUnitTools.prints_verbose(\"----------------------------------------------------------------\")\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitSingleton.gd.uid",
    "content": "uid://blkbl1d8o6h6w\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitTestSuiteBuilder.gd",
    "content": "class_name GdUnitTestSuiteBuilder\nextends RefCounted\n\n\nstatic func create(source :Script, line_number :int) -> GdUnitResult:\n\tvar test_suite_path := GdUnitTestSuiteScanner.resolve_test_suite_path(source.resource_path, GdUnitSettings.test_root_folder())\n\t# we need to save and close the testsuite and source if is current opened before modify\n\t@warning_ignore(\"return_value_discarded\")\n\tScriptEditorControls.save_an_open_script(source.resource_path)\n\t@warning_ignore(\"return_value_discarded\")\n\tScriptEditorControls.save_an_open_script(test_suite_path, true)\n\tif GdObjects.is_cs_script(source):\n\t\treturn GdUnit4CSharpApiLoader.create_test_suite(source.resource_path, line_number+1, test_suite_path)\n\tvar parser := GdScriptParser.new()\n\tvar lines := source.source_code.split(\"\\n\")\n\tvar current_line := lines[line_number]\n\tvar func_name := parser.parse_func_name(current_line)\n\tif func_name.is_empty():\n\t\treturn GdUnitResult.error(\"No function found at line: %d.\" % line_number)\n\treturn GdUnitTestSuiteScanner.create_test_case(test_suite_path, func_name, source.resource_path)\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitTestSuiteBuilder.gd.uid",
    "content": "uid://pxvyod2o5cs4\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd",
    "content": "class_name GdUnitTestSuiteScanner\nextends RefCounted\n\nconst TEST_FUNC_TEMPLATE =\"\"\"\n\nfunc test_${func_name}() -> void:\n\t# remove this line and complete your test\n\tassert_not_yet_implemented()\n\"\"\"\n\n\n# we exclude the gdunit source directorys by default\nconst exclude_scan_directories = [\n\t\"res://addons/gdUnit4/bin\",\n\t\"res://addons/gdUnit4/src\",\n\t\"res://reports\"]\n\n\nvar _script_parser := GdScriptParser.new()\nvar _included_resources :PackedStringArray = []\nvar _excluded_resources :PackedStringArray = []\nvar _expression_runner := GdUnitExpressionRunner.new()\nvar _regex_extends_clazz_name := RegEx.create_from_string(\"extends[\\\\s]+([\\\\S]+)\")\n\n\nfunc prescan_testsuite_classes() -> void:\n\t# scan and cache extends GdUnitTestSuite by class name an resource paths\n\tvar script_classes :Array[Dictionary] = ProjectSettings.get_global_class_list()\n\tfor script_meta in script_classes:\n\t\tvar base_class :String = script_meta[\"base\"]\n\t\tvar resource_path :String = script_meta[\"path\"]\n\t\tif base_class == \"GdUnitTestSuite\":\n\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t_included_resources.append(resource_path)\n\t\telif ClassDB.class_exists(base_class):\n\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t_excluded_resources.append(resource_path)\n\n\nfunc scan(resource_path :String) -> Array[Node]:\n\tprescan_testsuite_classes()\n\t# if single testsuite requested\n\tif FileAccess.file_exists(resource_path):\n\t\tvar test_suite := _parse_is_test_suite(resource_path)\n\t\tif test_suite != null:\n\t\t\treturn [test_suite]\n\t\treturn [] as Array[Node]\n\tvar base_dir := DirAccess.open(resource_path)\n\tif base_dir == null:\n\t\t\tprints(\"Given directory or file does not exists:\", resource_path)\n\t\t\treturn []\n\treturn _scan_test_suites(base_dir, [])\n\n\nfunc _scan_test_suites(dir :DirAccess, collected_suites :Array[Node]) -> Array[Node]:\n\tif exclude_scan_directories.has(dir.get_current_dir()):\n\t\treturn collected_suites\n\tprints(\"Scanning for test suites in:\", dir.get_current_dir())\n\t@warning_ignore(\"return_value_discarded\")\n\tdir.list_dir_begin() # TODOGODOT4 fill missing arguments https://github.com/godotengine/godot/pull/40547\n\tvar file_name := dir.get_next()\n\twhile file_name != \"\":\n\t\tvar resource_path := GdUnitTestSuiteScanner._file(dir, file_name)\n\t\tif dir.current_is_dir():\n\t\t\tvar sub_dir := DirAccess.open(resource_path)\n\t\t\tif sub_dir != null:\n\t\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t\t_scan_test_suites(sub_dir, collected_suites)\n\t\telse:\n\t\t\tvar time := LocalTime.now()\n\t\t\tvar test_suite := _parse_is_test_suite(resource_path)\n\t\t\tif test_suite:\n\t\t\t\tcollected_suites.append(test_suite)\n\t\t\tif OS.is_stdout_verbose() and time.elapsed_since_ms() > 300:\n\t\t\t\tpush_warning(\"Scanning of test-suite '%s' took more than 300ms: \" % resource_path, time.elapsed_since())\n\t\tfile_name = dir.get_next()\n\treturn collected_suites\n\n\nstatic func _file(dir :DirAccess, file_name :String) -> String:\n\tvar current_dir := dir.get_current_dir()\n\tif current_dir.ends_with(\"/\"):\n\t\treturn current_dir + file_name\n\treturn current_dir + \"/\" + file_name\n\n\nfunc _parse_is_test_suite(resource_path :String) -> Node:\n\tif not GdUnitTestSuiteScanner._is_script_format_supported(resource_path):\n\t\treturn null\n\tif GdUnit4CSharpApiLoader.is_test_suite(resource_path):\n\t\treturn GdUnit4CSharpApiLoader.parse_test_suite(resource_path)\n\n\t# We use the global cache to fast scan for test suites.\n\tif _excluded_resources.has(resource_path):\n\t\treturn null\n\t# Check in the global class cache whether the GdUnitTestSuite class has been extended.\n\tif _included_resources.has(resource_path):\n\t\treturn _parse_test_suite(GdUnitTestSuiteScanner.load_with_disabled_warnings(resource_path))\n\n\t# Otherwise we need to scan manual, we need to exclude classes where direct extends form Godot classes\n\t# the resource loader can fail to load e.g. plugin classes with do preload other scripts\n\tvar extends_from := get_extends_classname(resource_path)\n\t# If not extends is defined or extends from a Godot class\n\tif extends_from.is_empty() or ClassDB.class_exists(extends_from):\n\t\treturn null\n\t# Finally, we need to load the class to determine it is a test suite\n\tvar script := GdUnitTestSuiteScanner.load_with_disabled_warnings(resource_path)\n\tif not GdObjects.is_test_suite(script):\n\t\treturn null\n\treturn _parse_test_suite(script)\n\n\n# We load the test suites with disabled unsafe_method_access to avoid spamming loading errors\n# `unsafe_method_access` will happen when using `assert_that`\nstatic func load_with_disabled_warnings(resource_path: String) -> GDScript:\n\t# grap current level\n\tvar unsafe_method_access: Variant = ProjectSettings.get_setting(\"debug/gdscript/warnings/unsafe_method_access\")\n\n\t# disable and load the script\n\tProjectSettings.set_setting(\"debug/gdscript/warnings/unsafe_method_access\", 0)\n\tvar script: GDScript = ResourceLoader.load(resource_path)\n\n\t# restore\n\tProjectSettings.set_setting(\"debug/gdscript/warnings/unsafe_method_access\", unsafe_method_access)\n\treturn script\n\n\nstatic func _is_script_format_supported(resource_path :String) -> bool:\n\tvar ext := resource_path.get_extension()\n\tif ext == \"gd\":\n\t\treturn true\n\treturn GdUnit4CSharpApiLoader.is_csharp_file(resource_path)\n\n\nfunc _parse_test_suite(script: Script) -> GdUnitTestSuite:\n\tif not GdObjects.is_test_suite(script):\n\t\treturn null\n\n\t# If test suite a C# script\n\tif GdUnit4CSharpApiLoader.is_test_suite(script.resource_path):\n\t\treturn GdUnit4CSharpApiLoader.parse_test_suite(script.resource_path)\n\n\t# Do pares as GDScript\n\tvar test_suite: GdUnitTestSuite = (script as GDScript).new()\n\ttest_suite.set_name(GdUnitTestSuiteScanner.parse_test_suite_name(script))\n\t# add test cases to test suite and parse test case line nummber\n\tvar test_case_names := _extract_test_case_names(script as GDScript)\n\t_parse_and_add_test_cases(test_suite, script as GDScript, test_case_names)\n\treturn test_suite\n\n\nfunc _extract_test_case_names(script :GDScript) -> PackedStringArray:\n\treturn script.get_script_method_list()\\\n\t\t.map(func(descriptor: Dictionary) -> String: return descriptor[\"name\"])\\\n\t\t.filter(func(func_name: String) -> bool: return func_name.begins_with(\"test\"))\n\n\nstatic func parse_test_suite_name(script :Script) -> String:\n\treturn script.resource_path.get_file().replace(\".gd\", \"\")\n\n\nfunc _handle_test_suite_arguments(test_suite: GdUnitTestSuite, script: GDScript, fd: GdFunctionDescriptor) -> void:\n\tfor arg in fd.args():\n\t\tmatch arg.name():\n\t\t\t_TestCase.ARGUMENT_SKIP:\n\t\t\t\tvar result: Variant = _expression_runner.execute(script, arg.plain_value())\n\t\t\t\tif result is bool:\n\t\t\t\t\ttest_suite.__is_skipped = result\n\t\t\t\telse:\n\t\t\t\t\tpush_error(\"Test expression '%s' cannot be evaluated because it is not of type bool!\" % arg.plain_value())\n\t\t\t_TestCase.ARGUMENT_SKIP_REASON:\n\t\t\t\ttest_suite.__skip_reason = arg.plain_value()\n\t\t\t_:\n\t\t\t\tpush_error(\"Unsuported argument `%s` found on before() at '%s'!\" % [arg.name(), script.resource_path])\n\n\nfunc _handle_test_case_arguments(test_suite: GdUnitTestSuite, script: GDScript, fd: GdFunctionDescriptor) -> void:\n\tvar timeout := _TestCase.DEFAULT_TIMEOUT\n\tvar iterations := Fuzzer.ITERATION_DEFAULT_COUNT\n\tvar seed_value := -1\n\tvar is_skipped := false\n\tvar skip_reason := \"Unknown.\"\n\tvar fuzzers: Array[GdFunctionArgument] = []\n\tvar test := _TestCase.new()\n\n\tfor arg: GdFunctionArgument in fd.args():\n\t\t# verify argument is allowed\n\t\t# is test using fuzzers?\n\t\tif arg.type() == GdObjects.TYPE_FUZZER:\n\t\t\tfuzzers.append(arg)\n\t\telif arg.has_default():\n\t\t\tmatch arg.name():\n\t\t\t\t_TestCase.ARGUMENT_TIMEOUT:\n\t\t\t\t\ttimeout = arg.default()\n\t\t\t\t_TestCase.ARGUMENT_SKIP:\n\t\t\t\t\tvar result :Variant = _expression_runner.execute(script, arg.plain_value())\n\t\t\t\t\tif result is bool:\n\t\t\t\t\t\tis_skipped = result\n\t\t\t\t\telse:\n\t\t\t\t\t\tpush_error(\"Test expression '%s' cannot be evaluated because it is not of type bool!\" % arg.plain_value())\n\t\t\t\t_TestCase.ARGUMENT_SKIP_REASON:\n\t\t\t\t\tskip_reason = arg.plain_value()\n\t\t\t\tFuzzer.ARGUMENT_ITERATIONS:\n\t\t\t\t\titerations = arg.default()\n\t\t\t\tFuzzer.ARGUMENT_SEED:\n\t\t\t\t\tseed_value = arg.default()\n\t# create new test\n\t@warning_ignore(\"return_value_discarded\")\n\ttest.configure(fd.name(), fd.line_number(), fd.source_path(), timeout, fuzzers, iterations, seed_value)\n\ttest.set_function_descriptor(fd)\n\ttest.skip(is_skipped, skip_reason)\n\t_validate_argument(fd, test)\n\ttest_suite.add_child(test)\n\n\nfunc _parse_and_add_test_cases(test_suite: GdUnitTestSuite, script: GDScript, test_case_names: PackedStringArray) -> void:\n\tvar test_cases_to_find := Array(test_case_names)\n\tvar functions_to_scan := test_case_names.duplicate()\n\t@warning_ignore(\"return_value_discarded\")\n\tfunctions_to_scan.append(\"before\")\n\n\tvar function_descriptors := _script_parser.get_function_descriptors(script, functions_to_scan)\n\tfor fd in function_descriptors:\n\t\tif fd.name() == \"before\":\n\t\t\t_handle_test_suite_arguments(test_suite, script, fd)\n\t\tif test_cases_to_find.has(fd.name()):\n\t\t\t_handle_test_case_arguments(test_suite, script, fd)\n\n\nconst TEST_CASE_ARGUMENTS = [_TestCase.ARGUMENT_TIMEOUT, _TestCase.ARGUMENT_SKIP, _TestCase.ARGUMENT_SKIP_REASON, Fuzzer.ARGUMENT_ITERATIONS, Fuzzer.ARGUMENT_SEED]\n\nfunc _validate_argument(fd :GdFunctionDescriptor, test_case :_TestCase) -> void:\n\tif fd.is_parameterized():\n\t\treturn\n\tfor argument in fd.args():\n\t\tif argument.type() == GdObjects.TYPE_FUZZER or argument.name() in TEST_CASE_ARGUMENTS:\n\t\t\tcontinue\n\t\ttest_case.skip(true, \"Unknown test case argument '%s' found.\" % argument.name())\n\n\n# converts given file name by configured naming convention\nstatic func _to_naming_convention(file_name :String) -> String:\n\tvar nc :int = GdUnitSettings.get_setting(GdUnitSettings.TEST_SUITE_NAMING_CONVENTION, 0)\n\tmatch nc:\n\t\tGdUnitSettings.NAMING_CONVENTIONS.AUTO_DETECT:\n\t\t\tif GdObjects.is_snake_case(file_name):\n\t\t\t\treturn GdObjects.to_snake_case(file_name + \"Test\")\n\t\t\treturn GdObjects.to_pascal_case(file_name + \"Test\")\n\t\tGdUnitSettings.NAMING_CONVENTIONS.SNAKE_CASE:\n\t\t\treturn GdObjects.to_snake_case(file_name + \"Test\")\n\t\tGdUnitSettings.NAMING_CONVENTIONS.PASCAL_CASE:\n\t\t\treturn GdObjects.to_pascal_case(file_name + \"Test\")\n\tpush_error(\"Unexpected case\")\n\treturn \"-<Unexpected>-\"\n\n\nstatic func resolve_test_suite_path(source_script_path :String, test_root_folder :String = \"test\") -> String:\n\tvar file_name := source_script_path.get_basename().get_file()\n\tvar suite_name := _to_naming_convention(file_name)\n\tif test_root_folder.is_empty() or test_root_folder == \"/\":\n\t\treturn source_script_path.replace(file_name, suite_name)\n\n\t# is user tmp\n\tif source_script_path.begins_with(\"user://tmp\"):\n\t\treturn normalize_path(source_script_path.replace(\"user://tmp\", \"user://tmp/\" + test_root_folder)).replace(file_name, suite_name)\n\n\t# at first look up is the script under a \"src\" folder located\n\tvar test_suite_path :String\n\tvar src_folder := source_script_path.find(\"/src/\")\n\tif src_folder != -1:\n\t\ttest_suite_path = source_script_path.replace(\"/src/\", \"/\"+test_root_folder+\"/\")\n\telse:\n\t\tvar paths := source_script_path.split(\"/\", false)\n\t\t# is a plugin script?\n\t\tif paths[1] == \"addons\":\n\t\t\ttest_suite_path = \"%s//addons/%s/%s\" % [paths[0], paths[2], test_root_folder]\n\t\t\t# rebuild plugin path\n\t\t\tfor index in range(3, paths.size()):\n\t\t\t\ttest_suite_path += \"/\" + paths[index]\n\t\telse:\n\t\t\ttest_suite_path = paths[0] + \"//\" + test_root_folder\n\t\t\tfor index in range(1, paths.size()):\n\t\t\t\ttest_suite_path += \"/\" + paths[index]\n\treturn normalize_path(test_suite_path).replace(file_name, suite_name)\n\n\nstatic func normalize_path(path :String) -> String:\n\treturn path.replace(\"///\", \"/\")\n\n\nstatic func create_test_suite(test_suite_path :String, source_path :String) -> GdUnitResult:\n\t# create directory if not exists\n\tif not DirAccess.dir_exists_absolute(test_suite_path.get_base_dir()):\n\t\tvar error_ := DirAccess.make_dir_recursive_absolute(test_suite_path.get_base_dir())\n\t\tif error_ != OK:\n\t\t\treturn GdUnitResult.error(\"Can't create directoy  at: %s. Error code %s\" % [test_suite_path.get_base_dir(), error_])\n\tvar script := GDScript.new()\n\tscript.source_code = GdUnitTestSuiteTemplate.build_template(source_path)\n\tvar error := ResourceSaver.save(script, test_suite_path)\n\tif error != OK:\n\t\treturn GdUnitResult.error(\"Can't create test suite at: %s. Error code %s\" % [test_suite_path, error])\n\treturn GdUnitResult.success(test_suite_path)\n\n\nstatic func get_test_case_line_number(resource_path :String, func_name :String) -> int:\n\tvar file := FileAccess.open(resource_path, FileAccess.READ)\n\tif file != null:\n\t\tvar line_number := 0\n\t\twhile not file.eof_reached():\n\t\t\tvar row := file.get_line()\n\t\t\tline_number += 1\n\t\t\t# ignore comments and empty lines and not test functions\n\t\t\tif row.begins_with(\"#\") || row.length() == 0 || row.find(\"func test_\") == -1:\n\t\t\t\tcontinue\n\t\t\t# abort if test case name found\n\t\t\tif row.find(\"func\") != -1 and row.find(\"test_\" + func_name) != -1:\n\t\t\t\treturn line_number\n\treturn -1\n\n\nfunc get_extends_classname(resource_path :String) -> String:\n\tvar file := FileAccess.open(resource_path, FileAccess.READ)\n\tif file != null:\n\t\twhile not file.eof_reached():\n\t\t\tvar row := file.get_line()\n\t\t\t# skip comments and empty lines\n\t\t\tif row.begins_with(\"#\") || row.length() == 0:\n\t\t\t\tcontinue\n\t\t\t# Stop at first function\n\t\t\tif row.contains(\"func\"):\n\t\t\t\treturn \"\"\n\t\t\tvar result := _regex_extends_clazz_name.search(row)\n\t\t\tif result != null:\n\t\t\t\treturn result.get_string(1)\n\treturn \"\"\n\n\nstatic func add_test_case(resource_path :String, func_name :String)  -> GdUnitResult:\n\tvar script := load_with_disabled_warnings(resource_path)\n\t# count all exiting lines and add two as space to add new test case\n\tvar line_number := count_lines(script) + 2\n\tvar func_body := TEST_FUNC_TEMPLATE.replace(\"${func_name}\", func_name)\n\tif Engine.is_editor_hint():\n\t\tvar settings := EditorInterface.get_editor_settings()\n\t\tvar ident_type :int = settings.get_setting(\"text_editor/behavior/indent/type\")\n\t\tvar ident_size :int = settings.get_setting(\"text_editor/behavior/indent/size\")\n\t\tif ident_type == 1:\n\t\t\tfunc_body = func_body.replace(\"\t\", \"\".lpad(ident_size, \" \"))\n\tscript.source_code += func_body\n\tvar error := ResourceSaver.save(script, resource_path)\n\tif error != OK:\n\t\treturn GdUnitResult.error(\"Can't add test case at: %s to '%s'. Error code %s\" % [func_name, resource_path, error])\n\treturn GdUnitResult.success({ \"path\" : resource_path, \"line\" : line_number})\n\n\nstatic func count_lines(script : GDScript) -> int:\n\treturn script.source_code.split(\"\\n\").size()\n\n\nstatic func test_suite_exists(test_suite_path :String) -> bool:\n\treturn FileAccess.file_exists(test_suite_path)\n\nstatic func test_case_exists(test_suite_path :String, func_name :String) -> bool:\n\tif not test_suite_exists(test_suite_path):\n\t\treturn false\n\tvar script := load_with_disabled_warnings(test_suite_path)\n\tfor f in script.get_script_method_list():\n\t\tif f[\"name\"] == \"test_\" + func_name:\n\t\t\treturn true\n\treturn false\n\nstatic func create_test_case(test_suite_path :String, func_name :String, source_script_path :String) -> GdUnitResult:\n\tif test_case_exists(test_suite_path, func_name):\n\t\tvar line_number := get_test_case_line_number(test_suite_path, func_name)\n\t\treturn GdUnitResult.success({ \"path\" : test_suite_path, \"line\" : line_number})\n\n\tif not test_suite_exists(test_suite_path):\n\t\tvar result := create_test_suite(test_suite_path, source_script_path)\n\t\tif result.is_error():\n\t\t\treturn result\n\treturn add_test_case(test_suite_path, func_name)\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd.uid",
    "content": "uid://dvoleq3hg4218\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitTools.gd",
    "content": "extends RefCounted\n\n\nstatic var _richtext_normalize: RegEx\n\n\nstatic func normalize_text(text :String) -> String:\n\treturn text.replace(\"\\r\", \"\");\n\n\nstatic func richtext_normalize(input :String) -> String:\n\tif _richtext_normalize == null:\n\t\t_richtext_normalize = to_regex(\"\\\\[/?(b|color|bgcolor|right|table|cell).*?\\\\]\")\n\treturn _richtext_normalize.sub(input, \"\", true).replace(\"\\r\", \"\")\n\n\nstatic func to_regex(pattern :String) -> RegEx:\n\tvar regex := RegEx.new()\n\tvar err := regex.compile(pattern)\n\tif err != OK:\n\t\tpush_error(\"Can't compiling regx '%s'.\\n ERROR: %s\" % [pattern, error_string(err)])\n\treturn regex\n\n\nstatic func prints_verbose(message :String) -> void:\n\tif OS.is_stdout_verbose():\n\t\tprints(message)\n\n\n@warning_ignore(\"unsafe_cast\")\nstatic func free_instance(instance :Variant, use_call_deferred :bool = false, is_stdout_verbose := false) -> bool:\n\tif instance is Array:\n\t\tfor element :Variant in instance:\n\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\tfree_instance(element)\n\t\t(instance as Array).clear()\n\t\treturn true\n\t# do not free an already freed instance\n\tif not is_instance_valid(instance):\n\t\treturn false\n\t# do not free a class refernece\n\tif typeof(instance) == TYPE_OBJECT and (instance as Object).is_class(\"GDScriptNativeClass\"):\n\t\treturn false\n\tif is_stdout_verbose:\n\t\tprint_verbose(\"GdUnit4:gc():free instance \", instance)\n\trelease_double(instance as Object)\n\tif instance is RefCounted:\n\t\t(instance as RefCounted).notification(Object.NOTIFICATION_PREDELETE)\n\t\t# If scene runner freed we explicit await all inputs are processed\n\t\tif instance is GdUnitSceneRunnerImpl:\n\t\t\tawait (instance as GdUnitSceneRunnerImpl).await_input_processed()\n\t\treturn true\n\telse:\n\t\tif instance is Timer:\n\t\t\tvar timer := instance as Timer\n\t\t\ttimer.stop()\n\t\t\tif use_call_deferred:\n\t\t\t\ttimer.call_deferred(\"free\")\n\t\t\telse:\n\t\t\t\ttimer.free()\n\t\t\t\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\t\t\treturn true\n\n\t\tif instance is Node and (instance as Node).get_parent() != null:\n\t\t\tvar node := instance as Node\n\t\t\tif is_stdout_verbose:\n\t\t\t\tprint_verbose(\"GdUnit4:gc():remove node from parent \", node.get_parent(), node)\n\t\t\tif use_call_deferred:\n\t\t\t\tnode.get_parent().remove_child.call_deferred(node)\n\t\t\t\t#instance.call_deferred(\"set_owner\", null)\n\t\t\telse:\n\t\t\t\tnode.get_parent().remove_child(node)\n\t\tif is_stdout_verbose:\n\t\t\tprint_verbose(\"GdUnit4:gc():freeing `free()` the instance \", instance)\n\t\tif use_call_deferred:\n\t\t\t(instance as Object).call_deferred(\"free\")\n\t\telse:\n\t\t\t(instance as Object).free()\n\t\treturn !is_instance_valid(instance)\n\n\nstatic func _release_connections(instance :Object) -> void:\n\tif is_instance_valid(instance):\n\t\t# disconnect from all connected signals to force freeing, otherwise it ends up in orphans\n\t\tfor connection in instance.get_incoming_connections():\n\t\t\tvar signal_ :Signal = connection[\"signal\"]\n\t\t\tvar callable_ :Callable = connection[\"callable\"]\n\t\t\t#prints(instance, connection)\n\t\t\t#prints(\"signal\", signal_.get_name(), signal_.get_object())\n\t\t\t#prints(\"callable\", callable_.get_object())\n\t\t\tif instance.has_signal(signal_.get_name()) and instance.is_connected(signal_.get_name(), callable_):\n\t\t\t\t#prints(\"disconnect signal\", signal_.get_name(), callable_)\n\t\t\t\tinstance.disconnect(signal_.get_name(), callable_)\n\trelease_timers()\n\n\nstatic func release_timers() -> void:\n\t# we go the new way to hold all gdunit timers in group 'GdUnitTimers'\n\tvar scene_tree := Engine.get_main_loop() as SceneTree\n\tif scene_tree.root == null:\n\t\treturn\n\tfor node :Node in scene_tree.root.get_children():\n\t\tif is_instance_valid(node) and node.is_in_group(\"GdUnitTimers\"):\n\t\t\tif is_instance_valid(node):\n\t\t\t\tscene_tree.root.remove_child.call_deferred(node)\n\t\t\t\t(node as Timer).stop()\n\t\t\t\tnode.queue_free()\n\n\n# the finally cleaup unfreed resources and singletons\nstatic func dispose_all(use_call_deferred :bool = false) -> void:\n\trelease_timers()\n\tGdUnitSingleton.dispose(use_call_deferred)\n\tGdUnitSignals.dispose()\n\n\n# if instance an mock or spy we need manually freeing the self reference\nstatic func release_double(instance :Object) -> void:\n\tif instance.has_method(\"__release_double\"):\n\t\tinstance.call(\"__release_double\")\n\n\nstatic func register_expect_interupted_by_timeout(test_suite :Node, test_case_name :String) -> void:\n\tvar test_case: _TestCase = test_suite.find_child(test_case_name, false, false)\n\ttest_case.expect_to_interupt()\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GdUnitTools.gd.uid",
    "content": "uid://baklr6vat4021\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GodotVersionFixures.gd",
    "content": "## This service class contains helpers to wrap Godot functions and handle them carefully depending on the current Godot version\nclass_name GodotVersionFixures\nextends RefCounted\n\n\n@warning_ignore(\"shadowed_global_identifier\")\nstatic func type_convert(value: Variant, type: int) -> Variant:\n\treturn convert(value, type)\n\n\n@warning_ignore(\"shadowed_global_identifier\")\nstatic func convert(value: Variant, type: int) -> Variant:\n\treturn type_convert(value, type)\n\n\n# handle global_position fixed by https://github.com/godotengine/godot/pull/88473\nstatic func set_event_global_position(event: InputEventMouseMotion, global_position: Vector2) -> void:\n\tif Engine.get_version_info().hex >= 0x40202 or Engine.get_version_info().hex == 0x40104:\n\t\tevent.global_position = event.position\n\telse:\n\t\tevent.global_position = global_position\n"
  },
  {
    "path": "addons/gdUnit4/src/core/GodotVersionFixures.gd.uid",
    "content": "uid://dkqo6k5uvv180\n"
  },
  {
    "path": "addons/gdUnit4/src/core/LocalTime.gd",
    "content": "# This class provides Date/Time functionallity to Godot\nclass_name LocalTime\nextends Resource\n\nenum TimeUnit {\n\tDEFAULT = 0,\n\tMILLIS = 1,\n\tSECOND = 2,\n\tMINUTE = 3,\n\tHOUR   = 4,\n\tDAY    = 5,\n\tMONTH  = 6,\n\tYEAR   = 7\n}\n\nconst SECONDS_PER_MINUTE:int = 60\nconst MINUTES_PER_HOUR:int = 60\nconst HOURS_PER_DAY:int = 24\nconst MILLIS_PER_SECOND:int = 1000\nconst MILLIS_PER_MINUTE:int = MILLIS_PER_SECOND * SECONDS_PER_MINUTE\nconst MILLIS_PER_HOUR:int   = MILLIS_PER_MINUTE * MINUTES_PER_HOUR\n\nvar _time :int\nvar _hour :int\nvar _minute :int\nvar _second :int\nvar _millisecond :int\n\n\nstatic func now() -> LocalTime:\n\treturn LocalTime.new(_get_system_time_msecs())\n\n\nstatic func of_unix_time(time_ms :int) -> LocalTime:\n\treturn LocalTime.new(time_ms)\n\n\nstatic func local_time(hours :int, minutes :int, seconds :int, milliseconds :int) -> LocalTime:\n\treturn LocalTime.new(MILLIS_PER_HOUR * hours\\\n\t\t+ MILLIS_PER_MINUTE * minutes\\\n\t\t+ MILLIS_PER_SECOND * seconds\\\n\t\t+ milliseconds)\n\n\nfunc elapsed_since() -> String:\n\treturn LocalTime.elapsed(LocalTime._get_system_time_msecs() - _time)\n\n\nfunc elapsed_since_ms() -> int:\n\treturn LocalTime._get_system_time_msecs() - _time\n\n\nfunc plus(time_unit :TimeUnit, value :int) -> LocalTime:\n\tvar addValue:int = 0\n\tmatch time_unit:\n\t\tTimeUnit.MILLIS:\n\t\t\taddValue = value\n\t\tTimeUnit.SECOND:\n\t\t\taddValue = value * MILLIS_PER_SECOND\n\t\tTimeUnit.MINUTE:\n\t\t\taddValue = value * MILLIS_PER_MINUTE\n\t\tTimeUnit.HOUR:\n\t\t\taddValue = value * MILLIS_PER_HOUR\n\t@warning_ignore(\"return_value_discarded\")\n\t_init(_time + addValue)\n\treturn self\n\n\nstatic func elapsed(p_time_ms :int) -> String:\n\tvar local_time_ := LocalTime.new(p_time_ms)\n\tif local_time_._hour > 0:\n\t\treturn \"%dh %dmin %ds %dms\" % [local_time_._hour, local_time_._minute, local_time_._second, local_time_._millisecond]\n\tif local_time_._minute > 0:\n\t\treturn \"%dmin %ds %dms\" % [local_time_._minute, local_time_._second, local_time_._millisecond]\n\tif local_time_._second > 0:\n\t\treturn \"%ds %dms\" % [local_time_._second, local_time_._millisecond]\n\treturn \"%dms\" % local_time_._millisecond\n\n\n@warning_ignore(\"integer_division\")\n# create from epoch timestamp in ms\nfunc _init(time :int) -> void:\n\t_time = time\n\t_hour  =  (time / MILLIS_PER_HOUR) % 24\n\t_minute =  (time / MILLIS_PER_MINUTE) % 60\n\t_second =  (time / MILLIS_PER_SECOND) % 60\n\t_millisecond = time % 1000\n\n\nfunc hour() -> int:\n\treturn _hour\n\n\nfunc minute() -> int:\n\treturn _minute\n\n\nfunc second() -> int:\n\treturn _second\n\n\nfunc millis() -> int:\n\treturn _millisecond\n\n\nfunc _to_string() -> String:\n\treturn \"%02d:%02d:%02d.%03d\" % [_hour, _minute, _second, _millisecond]\n\n\n# wraper to old OS.get_system_time_msecs() function\nstatic func _get_system_time_msecs() -> int:\n\treturn Time.get_unix_time_from_system() * 1000 as int\n"
  },
  {
    "path": "addons/gdUnit4/src/core/LocalTime.gd.uid",
    "content": "uid://b0j7bpvdh6tve\n"
  },
  {
    "path": "addons/gdUnit4/src/core/_TestCase.gd",
    "content": "class_name _TestCase\nextends Node\n\nsignal completed()\n\n# default timeout 5min\nconst DEFAULT_TIMEOUT := -1\nconst ARGUMENT_TIMEOUT := \"timeout\"\nconst ARGUMENT_SKIP := \"do_skip\"\nconst ARGUMENT_SKIP_REASON := \"skip_reason\"\n\nvar _iterations: int = 1\nvar _current_iteration: int = -1\nvar _seed: int\nvar _fuzzers: Array[GdFunctionArgument] = []\nvar _test_param_index := -1\nvar _line_number: int = -1\nvar _script_path: String\nvar _skipped := false\nvar _skip_reason := \"\"\nvar _expect_to_interupt := false\nvar _timer: Timer\nvar _interupted: bool = false\nvar _failed := false\nvar _parameter_set_resolver: GdUnitTestParameterSetResolver\nvar _is_disposed := false\n\nvar timeout: int = DEFAULT_TIMEOUT:\n\tset(value):\n\t\ttimeout = value\n\tget:\n\t\tif timeout == DEFAULT_TIMEOUT:\n\t\t\ttimeout = GdUnitSettings.test_timeout()\n\t\treturn timeout\n\n\n@warning_ignore(\"shadowed_variable_base_class\")\nfunc configure(p_name: String, p_line_number: int, p_script_path: String, p_timeout: int=DEFAULT_TIMEOUT, p_fuzzers: Array[GdFunctionArgument]=[], p_iterations: int=1, p_seed: int=-1) -> _TestCase:\n\tset_name(p_name)\n\t_line_number = p_line_number\n\t_fuzzers = p_fuzzers\n\t_iterations = p_iterations\n\t_seed = p_seed\n\t_script_path = p_script_path\n\ttimeout = p_timeout\n\treturn self\n\n\nfunc execute(p_test_parameter := Array(), p_iteration := 0) -> void:\n\t_failure_received(false)\n\t_current_iteration = p_iteration - 1\n\tif _current_iteration == - 1:\n\t\t_set_failure_handler()\n\t\tset_timeout()\n\tif not p_test_parameter.is_empty():\n\t\tupdate_fuzzers(p_test_parameter, p_iteration)\n\t\t_execute_test_case(name, p_test_parameter)\n\telse:\n\t\t_execute_test_case(name, [])\n\tawait completed\n\n\nfunc execute_paramaterized(p_test_parameter: Array) -> void:\n\t_failure_received(false)\n\tset_timeout()\n\t# We need here to add a empty array to override the `test_parameters` to prevent initial \"default\" parameters from being used.\n\t# This prevents objects in the argument list from being unnecessarily re-instantiated.\n\tvar test_parameters := p_test_parameter.duplicate() # is strictly need to duplicate the paramters before extend\n\ttest_parameters.append([])\n\t_execute_test_case(name, test_parameters)\n\tawait completed\n\n\nfunc dispose() -> void:\n\tif _is_disposed:\n\t\treturn\n\t_is_disposed = true\n\tEngine.remove_meta(\"GD_TEST_FAILURE\")\n\tstop_timer()\n\t_remove_failure_handler()\n\t_fuzzers.clear()\n\n\n@warning_ignore(\"shadowed_variable_base_class\", \"redundant_await\")\nfunc _execute_test_case(name: String, test_parameter: Array) -> void:\n\t# needs at least on await otherwise it breaks the awaiting chain\n\tawait get_parent().callv(name, test_parameter)\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\tcompleted.emit()\n\n\nfunc update_fuzzers(input_values: Array, iteration: int) -> void:\n\tfor fuzzer :Variant in input_values:\n\t\tif fuzzer is Fuzzer:\n\t\t\tfuzzer._iteration_index = iteration + 1\n\n\nfunc set_timeout() -> void:\n\tif is_instance_valid(_timer):\n\t\treturn\n\tvar time: float = timeout / 1000.0\n\t_timer = Timer.new()\n\tadd_child(_timer)\n\t_timer.set_name(\"gdunit_test_case_timer_%d\" % _timer.get_instance_id())\n\t@warning_ignore(\"return_value_discarded\")\n\t_timer.timeout.connect(do_interrupt, CONNECT_DEFERRED)\n\t_timer.set_one_shot(true)\n\t_timer.set_wait_time(time)\n\t_timer.set_autostart(false)\n\t_timer.start()\n\n\nfunc do_interrupt() -> void:\n\t_interupted = true\n\tif not is_expect_interupted():\n\t\tvar execution_context:= GdUnitThreadManager.get_current_context().get_execution_context()\n\t\tif is_fuzzed():\n\t\t\texecution_context.add_report(GdUnitReport.new()\\\n\t\t\t\t.create(GdUnitReport.INTERUPTED, line_number(), GdAssertMessages.fuzzer_interuped(_current_iteration, \"timedout\")))\n\t\telse:\n\t\t\texecution_context.add_report(GdUnitReport.new()\\\n\t\t\t\t.create(GdUnitReport.INTERUPTED, line_number(), GdAssertMessages.test_timeout(timeout)))\n\tcompleted.emit()\n\n\nfunc _set_failure_handler() -> void:\n\tif not GdUnitSignals.instance().gdunit_set_test_failed.is_connected(_failure_received):\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tGdUnitSignals.instance().gdunit_set_test_failed.connect(_failure_received)\n\n\nfunc _remove_failure_handler() -> void:\n\tif GdUnitSignals.instance().gdunit_set_test_failed.is_connected(_failure_received):\n\t\tGdUnitSignals.instance().gdunit_set_test_failed.disconnect(_failure_received)\n\n\nfunc _failure_received(is_failed: bool) -> void:\n\t# is already failed?\n\tif _failed:\n\t\treturn\n\t_failed = is_failed\n\tEngine.set_meta(\"GD_TEST_FAILURE\", is_failed)\n\n\nfunc stop_timer() -> void:\n\t# finish outstanding timeouts\n\tif is_instance_valid(_timer):\n\t\t_timer.stop()\n\t\t_timer.call_deferred(\"free\")\n\t\t_timer = null\n\n\nfunc expect_to_interupt() -> void:\n\t_expect_to_interupt = true\n\n\nfunc is_interupted() -> bool:\n\treturn _interupted\n\n\nfunc is_expect_interupted() -> bool:\n\treturn _expect_to_interupt\n\n\nfunc is_parameterized() -> bool:\n\treturn _parameter_set_resolver.is_parameterized()\n\n\nfunc is_skipped() -> bool:\n\treturn _skipped\n\n\nfunc skip_info() -> String:\n\treturn _skip_reason\n\n\nfunc line_number() -> int:\n\treturn _line_number\n\n\nfunc iterations() -> int:\n\treturn _iterations\n\n\nfunc seed_value() -> int:\n\treturn _seed\n\n\nfunc is_fuzzed() -> bool:\n\treturn not _fuzzers.is_empty()\n\n\nfunc fuzzer_arguments() -> Array[GdFunctionArgument]:\n\treturn _fuzzers\n\n\nfunc script_path() -> String:\n\treturn _script_path\n\n\nfunc ResourcePath() -> String:\n\treturn _script_path\n\n\nfunc generate_seed() -> void:\n\tif _seed != -1:\n\t\tseed(_seed)\n\n\nfunc skip(skipped: bool, reason: String=\"\") -> void:\n\t_skipped = skipped\n\t_skip_reason = reason\n\n\nfunc set_function_descriptor(fd: GdFunctionDescriptor) -> void:\n\t_parameter_set_resolver = GdUnitTestParameterSetResolver.new(fd)\n\n\nfunc set_test_parameter_index(index: int) -> void:\n\t_test_param_index = index\n\n\nfunc test_parameter_index() -> int:\n\treturn _test_param_index\n\n\nfunc test_case_names() -> PackedStringArray:\n\treturn _parameter_set_resolver.build_test_case_names(self)\n\n\nfunc load_parameter_sets() -> Array:\n\treturn _parameter_set_resolver.load_parameter_sets(self, true)\n\n\nfunc parameter_set_resolver() -> GdUnitTestParameterSetResolver:\n\treturn _parameter_set_resolver\n\n\nfunc _to_string() -> String:\n\treturn \"%s :%d (%dms)\" % [get_name(), _line_number, timeout]\n"
  },
  {
    "path": "addons/gdUnit4/src/core/_TestCase.gd.uid",
    "content": "uid://4u8xyk0gp46u\n"
  },
  {
    "path": "addons/gdUnit4/src/core/assets/touch-button.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://btx5kcrsngasl\"\npath=\"res://.godot/imported/touch-button.png-2fff40c8520d8e97a57db1b2b043f641.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/gdUnit4/src/core/assets/touch-button.png\"\ndest_files=[\"res://.godot/imported/touch-button.png-2fff40c8520d8e97a57db1b2b043f641.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/gdUnit4/src/core/command/GdUnitCommand.gd",
    "content": "class_name GdUnitCommand\nextends RefCounted\n\n\nfunc _init(p_name :String, p_is_enabled: Callable, p_runnable: Callable, p_shortcut :GdUnitShortcut.ShortCut = GdUnitShortcut.ShortCut.NONE) -> void:\n\tassert(p_name != null, \"(%s) missing parameter 'name'\" % p_name)\n\tassert(p_is_enabled != null, \"(%s) missing parameter 'is_enabled'\" % p_name)\n\tassert(p_runnable != null, \"(%s) missing parameter 'runnable'\" % p_name)\n\tassert(p_shortcut != null, \"(%s) missing parameter 'shortcut'\" % p_name)\n\tself.name = p_name\n\tself.is_enabled = p_is_enabled\n\tself.shortcut = p_shortcut\n\tself.runnable = p_runnable\n\n\nvar name: String:\n\tset(value):\n\t\tname = value\n\tget:\n\t\treturn name\n\n\nvar shortcut: GdUnitShortcut.ShortCut:\n\tset(value):\n\t\tshortcut = value\n\tget:\n\t\treturn shortcut\n\n\nvar is_enabled: Callable:\n\tset(value):\n\t\tis_enabled = value\n\tget:\n\t\treturn is_enabled\n\n\nvar runnable: Callable:\n\tset(value):\n\t\trunnable = value\n\tget:\n\t\treturn runnable\n"
  },
  {
    "path": "addons/gdUnit4/src/core/command/GdUnitCommand.gd.uid",
    "content": "uid://61ky27ug1ray\n"
  },
  {
    "path": "addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd",
    "content": "class_name GdUnitCommandHandler\nextends Object\n\nsignal gdunit_runner_start()\nsignal gdunit_runner_stop(client_id :int)\n\n\nconst GdUnitTools := preload(\"res://addons/gdUnit4/src/core/GdUnitTools.gd\")\n\nconst CMD_RUN_OVERALL = \"Debug Overall TestSuites\"\nconst CMD_RUN_TESTCASE = \"Run TestCases\"\nconst CMD_RUN_TESTCASE_DEBUG = \"Run TestCases (Debug)\"\nconst CMD_RUN_TESTSUITE = \"Run TestSuites\"\nconst CMD_RUN_TESTSUITE_DEBUG = \"Run TestSuites (Debug)\"\nconst CMD_RERUN_TESTS = \"ReRun Tests\"\nconst CMD_RERUN_TESTS_DEBUG = \"ReRun Tests (Debug)\"\nconst CMD_STOP_TEST_RUN = \"Stop Test Run\"\nconst CMD_CREATE_TESTCASE = \"Create TestCase\"\n\nconst SETTINGS_SHORTCUT_MAPPING := {\n\t\"N/A\" : GdUnitShortcut.ShortCut.NONE,\n\tGdUnitSettings.SHORTCUT_INSPECTOR_RERUN_TEST : GdUnitShortcut.ShortCut.RERUN_TESTS,\n\tGdUnitSettings.SHORTCUT_INSPECTOR_RERUN_TEST_DEBUG : GdUnitShortcut.ShortCut.RERUN_TESTS_DEBUG,\n\tGdUnitSettings.SHORTCUT_INSPECTOR_RUN_TEST_OVERALL : GdUnitShortcut.ShortCut.RUN_TESTS_OVERALL,\n\tGdUnitSettings.SHORTCUT_INSPECTOR_RUN_TEST_STOP : GdUnitShortcut.ShortCut.STOP_TEST_RUN,\n\tGdUnitSettings.SHORTCUT_EDITOR_RUN_TEST : GdUnitShortcut.ShortCut.RUN_TESTCASE,\n\tGdUnitSettings.SHORTCUT_EDITOR_RUN_TEST_DEBUG : GdUnitShortcut.ShortCut.RUN_TESTCASE_DEBUG,\n\tGdUnitSettings.SHORTCUT_EDITOR_CREATE_TEST : GdUnitShortcut.ShortCut.CREATE_TEST,\n\tGdUnitSettings.SHORTCUT_FILESYSTEM_RUN_TEST : GdUnitShortcut.ShortCut.RUN_TESTCASE,\n\tGdUnitSettings.SHORTCUT_FILESYSTEM_RUN_TEST_DEBUG : GdUnitShortcut.ShortCut.RUN_TESTCASE_DEBUG\n}\n\n# the current test runner config\nvar _runner_config := GdUnitRunnerConfig.new()\n\n# holds the current connected gdUnit runner client id\nvar _client_id: int\n# if no debug mode we have an process id\nvar _current_runner_process_id: int = 0\n# hold is current an test running\nvar _is_running: bool = false\n# holds if the current running tests started in debug mode\nvar _running_debug_mode: bool\n\nvar _commands := {}\nvar _shortcuts := {}\n\n\nstatic func instance() -> GdUnitCommandHandler:\n\treturn GdUnitSingleton.instance(\"GdUnitCommandHandler\", func() -> GdUnitCommandHandler: return GdUnitCommandHandler.new())\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc _init() -> void:\n\tassert_shortcut_mappings(SETTINGS_SHORTCUT_MAPPING)\n\n\tGdUnitSignals.instance().gdunit_event.connect(_on_event)\n\tGdUnitSignals.instance().gdunit_client_connected.connect(_on_client_connected)\n\tGdUnitSignals.instance().gdunit_client_disconnected.connect(_on_client_disconnected)\n\tGdUnitSignals.instance().gdunit_settings_changed.connect(_on_settings_changed)\n\t# preload previous test execution\n\t@warning_ignore(\"return_value_discarded\")\n\t_runner_config.load_config()\n\n\tinit_shortcuts()\n\tvar is_running := func(_script :Script) -> bool: return _is_running\n\tvar is_not_running := func(_script :Script) -> bool: return !_is_running\n\tregister_command(GdUnitCommand.new(CMD_RUN_OVERALL, is_not_running, cmd_run_overall.bind(true), GdUnitShortcut.ShortCut.RUN_TESTS_OVERALL))\n\tregister_command(GdUnitCommand.new(CMD_RUN_TESTCASE, is_not_running, cmd_editor_run_test.bind(false), GdUnitShortcut.ShortCut.RUN_TESTCASE))\n\tregister_command(GdUnitCommand.new(CMD_RUN_TESTCASE_DEBUG, is_not_running, cmd_editor_run_test.bind(true), GdUnitShortcut.ShortCut.RUN_TESTCASE_DEBUG))\n\tregister_command(GdUnitCommand.new(CMD_RUN_TESTSUITE, is_not_running, cmd_run_test_suites.bind(false)))\n\tregister_command(GdUnitCommand.new(CMD_RUN_TESTSUITE_DEBUG, is_not_running, cmd_run_test_suites.bind(true)))\n\tregister_command(GdUnitCommand.new(CMD_RERUN_TESTS, is_not_running, cmd_run.bind(false), GdUnitShortcut.ShortCut.RERUN_TESTS))\n\tregister_command(GdUnitCommand.new(CMD_RERUN_TESTS_DEBUG, is_not_running, cmd_run.bind(true), GdUnitShortcut.ShortCut.RERUN_TESTS_DEBUG))\n\tregister_command(GdUnitCommand.new(CMD_CREATE_TESTCASE, is_not_running, cmd_create_test, GdUnitShortcut.ShortCut.CREATE_TEST))\n\tregister_command(GdUnitCommand.new(CMD_STOP_TEST_RUN, is_running, cmd_stop.bind(_client_id), GdUnitShortcut.ShortCut.STOP_TEST_RUN))\n\n\t# schedule discover tests if enabled and running inside the editor\n\tif Engine.is_editor_hint() and GdUnitSettings.is_test_discover_enabled():\n\t\tvar timer :SceneTreeTimer = (Engine.get_main_loop() as SceneTree).create_timer(5)\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\ttimer.timeout.connect(cmd_discover_tests)\n\n\nfunc _notification(what: int) -> void:\n\tif what == NOTIFICATION_PREDELETE:\n\t\t_commands.clear()\n\t\t_shortcuts.clear()\n\n\nfunc _do_process() -> void:\n\tcheck_test_run_stopped_manually()\n\n\n# is checking if the user has press the editor stop scene\nfunc check_test_run_stopped_manually() -> void:\n\tif is_test_running_but_stop_pressed():\n\t\tif GdUnitSettings.is_verbose_assert_warnings():\n\t\t\tpush_warning(\"Test Runner scene was stopped manually, force stopping the current test run!\")\n\t\tcmd_stop(_client_id)\n\n\nfunc is_test_running_but_stop_pressed() -> bool:\n\treturn _running_debug_mode and _is_running and not EditorInterface.is_playing_scene()\n\n\nfunc assert_shortcut_mappings(mappings: Dictionary) -> void:\n\tfor shortcut: int in GdUnitShortcut.ShortCut.values():\n\t\tassert(mappings.values().has(shortcut), \"missing settings mapping for shortcut '%s'!\" % GdUnitShortcut.ShortCut.keys()[shortcut])\n\n\nfunc init_shortcuts() -> void:\n\tfor shortcut: int in GdUnitShortcut.ShortCut.values():\n\t\tif shortcut == GdUnitShortcut.ShortCut.NONE:\n\t\t\tcontinue\n\t\tvar property_name: String = SETTINGS_SHORTCUT_MAPPING.find_key(shortcut)\n\t\tvar property := GdUnitSettings.get_property(property_name)\n\t\tvar keys := GdUnitShortcut.default_keys(shortcut)\n\t\tif property != null:\n\t\t\tkeys = property.value()\n\t\tvar inputEvent := create_shortcut_input_even(keys)\n\t\tregister_shortcut(shortcut, inputEvent)\n\n\nfunc create_shortcut_input_even(key_codes: PackedInt32Array) -> InputEventKey:\n\tvar inputEvent := InputEventKey.new()\n\tinputEvent.pressed = true\n\tfor key_code in key_codes:\n\t\tmatch key_code:\n\t\t\tKEY_ALT:\n\t\t\t\tinputEvent.alt_pressed = true\n\t\t\tKEY_SHIFT:\n\t\t\t\tinputEvent.shift_pressed = true\n\t\t\tKEY_CTRL:\n\t\t\t\tinputEvent.ctrl_pressed = true\n\t\t\t_:\n\t\t\t\tinputEvent.keycode = key_code as Key\n\t\t\t\tinputEvent.physical_keycode = key_code as Key\n\treturn inputEvent\n\n\nfunc register_shortcut(p_shortcut: GdUnitShortcut.ShortCut, p_input_event: InputEvent) -> void:\n\tGdUnitTools.prints_verbose(\"register shortcut: '%s' to '%s'\" % [GdUnitShortcut.ShortCut.keys()[p_shortcut], p_input_event.as_text()])\n\tvar shortcut := Shortcut.new()\n\tshortcut.set_events([p_input_event])\n\tvar command_name := get_shortcut_command(p_shortcut)\n\t_shortcuts[p_shortcut] = GdUnitShortcutAction.new(p_shortcut, shortcut, command_name)\n\n\nfunc get_shortcut(shortcut_type: GdUnitShortcut.ShortCut) -> Shortcut:\n\treturn get_shortcut_action(shortcut_type).shortcut\n\n\nfunc get_shortcut_action(shortcut_type: GdUnitShortcut.ShortCut) -> GdUnitShortcutAction:\n\treturn _shortcuts.get(shortcut_type)\n\n\nfunc get_shortcut_command(p_shortcut: GdUnitShortcut.ShortCut) -> String:\n\treturn GdUnitShortcut.CommandMapping.get(p_shortcut, \"unknown command\")\n\n\nfunc register_command(p_command: GdUnitCommand) -> void:\n\t_commands[p_command.name] = p_command\n\n\nfunc command(cmd_name: String) -> GdUnitCommand:\n\treturn _commands.get(cmd_name)\n\n\nfunc cmd_run_test_suites(test_suite_paths: PackedStringArray, debug: bool, rerun := false) -> void:\n\t# create new runner runner_config for fresh run otherwise use saved one\n\tif not rerun:\n\t\tvar result := _runner_config.clear()\\\n\t\t\t.add_test_suites(test_suite_paths)\\\n\t\t\t.save_config()\n\t\tif result.is_error():\n\t\t\tpush_error(result.error_message())\n\t\t\treturn\n\tcmd_run(debug)\n\n\nfunc cmd_run_test_case(test_suite_resource_path: String, test_case: String, test_param_index: int, debug: bool, rerun := false) -> void:\n\t# create new runner config for fresh run otherwise use saved one\n\tif not rerun:\n\t\tvar result := _runner_config.clear()\\\n\t\t\t.add_test_case(test_suite_resource_path, test_case, test_param_index)\\\n\t\t\t.save_config()\n\t\tif result.is_error():\n\t\t\tpush_error(result.error_message())\n\t\t\treturn\n\tcmd_run(debug)\n\n\nfunc cmd_run_overall(debug: bool) -> void:\n\tvar test_suite_paths: PackedStringArray = GdUnitCommandHandler.scan_all_test_directories(GdUnitSettings.test_root_folder())\n\tvar result := _runner_config.clear()\\\n\t\t.add_test_suites(test_suite_paths)\\\n\t\t.save_config()\n\tif result.is_error():\n\t\tpush_error(result.error_message())\n\t\treturn\n\tcmd_run(debug)\n\n\nfunc cmd_run(debug: bool) -> void:\n\t# don't start is already running\n\tif _is_running:\n\t\treturn\n\t# save current selected excution config\n\tvar server_port: int = Engine.get_meta(\"gdunit_server_port\")\n\tvar result := _runner_config.set_server_port(server_port).save_config()\n\tif result.is_error():\n\t\tpush_error(result.error_message())\n\t\treturn\n\t# before start we have to save all changes\n\tScriptEditorControls.save_all_open_script()\n\tgdunit_runner_start.emit()\n\t_current_runner_process_id = -1\n\t_running_debug_mode = debug\n\tif debug:\n\t\trun_debug_mode()\n\telse:\n\t\trun_release_mode()\n\n\nfunc cmd_stop(client_id: int) -> void:\n\t# don't stop if is already stopped\n\tif not _is_running:\n\t\treturn\n\t_is_running = false\n\tgdunit_runner_stop.emit(client_id)\n\tif _running_debug_mode:\n\t\tEditorInterface.stop_playing_scene()\n\telif _current_runner_process_id > 0:\n\t\tif OS.is_process_running(_current_runner_process_id):\n\t\t\tvar result := OS.kill(_current_runner_process_id)\n\t\t\tif result != OK:\n\t\t\t\tpush_error(\"ERROR checked stopping GdUnit Test Runner. error code: %s\" % result)\n\t_current_runner_process_id = -1\n\n\nfunc cmd_editor_run_test(debug: bool) -> void:\n\tvar cursor_line := active_base_editor().get_caret_line()\n\t#run test case?\n\tvar regex := RegEx.new()\n\t@warning_ignore(\"return_value_discarded\")\n\tregex.compile(\"(^func[ ,\\t])(test_[a-zA-Z0-9_]*)\")\n\tvar result := regex.search(active_base_editor().get_line(cursor_line))\n\tif result:\n\t\tvar func_name := result.get_string(2).strip_edges()\n\t\tprints(\"Run test:\", func_name, \"debug\", debug)\n\t\tif func_name.begins_with(\"test_\"):\n\t\t\tcmd_run_test_case(active_script().resource_path, func_name, -1, debug)\n\t\t\treturn\n\t# otherwise run the full test suite\n\tvar selected_test_suites := [active_script().resource_path]\n\tcmd_run_test_suites(selected_test_suites, debug)\n\n\nfunc cmd_create_test() -> void:\n\tvar cursor_line := active_base_editor().get_caret_line()\n\tvar result := GdUnitTestSuiteBuilder.create(active_script(), cursor_line)\n\tif result.is_error():\n\t\t# show error dialog\n\t\tpush_error(\"Failed to create test case: %s\" % result.error_message())\n\t\treturn\n\tvar info: Dictionary = result.value()\n\tvar script_path: String = info.get(\"path\")\n\tvar script_line: int = info.get(\"line\")\n\tScriptEditorControls.edit_script(script_path, script_line)\n\n\nfunc cmd_discover_tests() -> void:\n\tawait GdUnitTestDiscoverer.run()\n\nstatic func scan_all_test_directories(root: String) -> PackedStringArray:\n\tvar base_directory := \"res://\"\n\t# If the test root folder is configured as blank, \"/\", or \"res://\", use the root folder as described in the settings panel\n\tif root.is_empty() or root == \"/\" or root == base_directory:\n\t\treturn [base_directory]\n\treturn scan_test_directories(base_directory, root, [])\n\nstatic func scan_test_directories(base_directory: String, test_directory: String, test_suite_paths: PackedStringArray) -> PackedStringArray:\n\tprint_verbose(\"Scannning for test directory '%s' at %s\" % [test_directory, base_directory])\n\tfor directory in DirAccess.get_directories_at(base_directory):\n\t\tif directory.begins_with(\".\"):\n\t\t\tcontinue\n\t\tvar current_directory := normalize_path(base_directory + \"/\" + directory)\n\t\tif GdUnitTestSuiteScanner.exclude_scan_directories.has(current_directory):\n\t\t\tcontinue\n\t\tif match_test_directory(directory, test_directory):\n\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\ttest_suite_paths.append(current_directory)\n\t\telse:\n\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\tscan_test_directories(current_directory, test_directory, test_suite_paths)\n\treturn test_suite_paths\n\n\nstatic func normalize_path(path: String) -> String:\n\treturn path.replace(\"///\", \"//\")\n\n\nstatic func match_test_directory(directory: String, test_directory: String) -> bool:\n\treturn directory == test_directory or test_directory.is_empty() or test_directory == \"/\" or test_directory == \"res://\"\n\n\nfunc run_debug_mode() -> void:\n\tEditorInterface.play_custom_scene(\"res://addons/gdUnit4/src/core/GdUnitRunner.tscn\")\n\t_is_running = true\n\n\nfunc run_release_mode() -> void:\n\tvar arguments := Array()\n\tif OS.is_stdout_verbose():\n\t\targuments.append(\"--verbose\")\n\targuments.append(\"--no-window\")\n\targuments.append(\"--path\")\n\targuments.append(ProjectSettings.globalize_path(\"res://\"))\n\targuments.append(\"res://addons/gdUnit4/src/core/GdUnitRunner.tscn\")\n\t_current_runner_process_id = OS.create_process(OS.get_executable_path(), arguments, false);\n\t_is_running = true\n\n\nfunc active_base_editor() -> TextEdit:\n\treturn EditorInterface.get_script_editor().get_current_editor().get_base_editor()\n\n\nfunc active_script() -> Script:\n\treturn EditorInterface.get_script_editor().get_current_script()\n\n\n\n################################################################################\n# signals handles\n################################################################################\nfunc _on_event(event: GdUnitEvent) -> void:\n\tif event.type() == GdUnitEvent.STOP:\n\t\tcmd_stop(_client_id)\n\n\nfunc _on_stop_pressed() -> void:\n\tcmd_stop(_client_id)\n\n\nfunc _on_run_pressed(debug := false) -> void:\n\tcmd_run(debug)\n\n\nfunc _on_run_overall_pressed(_debug := false) -> void:\n\tcmd_run_overall(true)\n\n\nfunc _on_settings_changed(property: GdUnitProperty) -> void:\n\tif SETTINGS_SHORTCUT_MAPPING.has(property.name()):\n\t\tvar shortcut :GdUnitShortcut.ShortCut = SETTINGS_SHORTCUT_MAPPING.get(property.name())\n\t\tvar value: PackedInt32Array = property.value()\n\t\tvar input_event := create_shortcut_input_even(value)\n\t\tprints(\"Shortcut changed: '%s' to '%s'\" % [GdUnitShortcut.ShortCut.keys()[shortcut], input_event.as_text()])\n\t\tregister_shortcut(shortcut, input_event)\n\tif property.name() == GdUnitSettings.TEST_DISCOVER_ENABLED:\n\t\tvar timer :SceneTreeTimer = (Engine.get_main_loop() as SceneTree).create_timer(3)\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\ttimer.timeout.connect(cmd_discover_tests)\n\n\n################################################################################\n# Network stuff\n################################################################################\nfunc _on_client_connected(client_id: int) -> void:\n\t_client_id = client_id\n\n\nfunc _on_client_disconnected(client_id: int) -> void:\n\t# only stops is not in debug mode running and the current client\n\tif not _running_debug_mode and _client_id == client_id:\n\t\tcmd_stop(client_id)\n\t_client_id = -1\n"
  },
  {
    "path": "addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd.uid",
    "content": "uid://bmxpnufp6mt7s\n"
  },
  {
    "path": "addons/gdUnit4/src/core/command/GdUnitShortcut.gd",
    "content": "class_name GdUnitShortcut\nextends RefCounted\n\n\nenum ShortCut {\n\tNONE,\n\tRUN_TESTS_OVERALL,\n\tRUN_TESTCASE,\n\tRUN_TESTCASE_DEBUG,\n\tRERUN_TESTS,\n\tRERUN_TESTS_DEBUG,\n\tSTOP_TEST_RUN,\n\tCREATE_TEST,\n}\n\n\nconst CommandMapping = {\n\tShortCut.RUN_TESTS_OVERALL: GdUnitCommandHandler.CMD_RUN_OVERALL,\n\tShortCut.RUN_TESTCASE: GdUnitCommandHandler.CMD_RUN_TESTCASE,\n\tShortCut.RUN_TESTCASE_DEBUG: GdUnitCommandHandler.CMD_RUN_TESTCASE_DEBUG,\n\tShortCut.RERUN_TESTS: GdUnitCommandHandler.CMD_RERUN_TESTS,\n\tShortCut.RERUN_TESTS_DEBUG: GdUnitCommandHandler.CMD_RERUN_TESTS_DEBUG,\n\tShortCut.STOP_TEST_RUN: GdUnitCommandHandler.CMD_STOP_TEST_RUN,\n\tShortCut.CREATE_TEST: GdUnitCommandHandler.CMD_CREATE_TESTCASE,\n}\n\n\nconst DEFAULTS_MACOS := {\n\tShortCut.NONE : [],\n\tShortCut.RUN_TESTCASE : [Key.KEY_META, Key.KEY_ALT, Key.KEY_F5],\n\tShortCut.RUN_TESTCASE_DEBUG : [Key.KEY_META, Key.KEY_ALT, Key.KEY_F6],\n\tShortCut.RUN_TESTS_OVERALL : [Key.KEY_META, Key.KEY_F7],\n\tShortCut.STOP_TEST_RUN : [Key.KEY_META, Key.KEY_F8],\n\tShortCut.RERUN_TESTS : [Key.KEY_META, Key.KEY_F5],\n\tShortCut.RERUN_TESTS_DEBUG : [Key.KEY_META, Key.KEY_F6],\n\tShortCut.CREATE_TEST : [Key.KEY_META, Key.KEY_ALT, Key.KEY_F10],\n}\n\nconst DEFAULTS_WINDOWS := {\n\tShortCut.NONE : [],\n\tShortCut.RUN_TESTCASE : [Key.KEY_CTRL, Key.KEY_ALT, Key.KEY_F5],\n\tShortCut.RUN_TESTCASE_DEBUG : [Key.KEY_CTRL,Key.KEY_ALT,  Key.KEY_F6],\n\tShortCut.RUN_TESTS_OVERALL : [Key.KEY_CTRL, Key.KEY_F7],\n\tShortCut.STOP_TEST_RUN : [Key.KEY_CTRL, Key.KEY_F8],\n\tShortCut.RERUN_TESTS : [Key.KEY_CTRL, Key.KEY_F5],\n\tShortCut.RERUN_TESTS_DEBUG : [Key.KEY_CTRL, Key.KEY_F6],\n\tShortCut.CREATE_TEST : [Key.KEY_CTRL, Key.KEY_ALT, Key.KEY_F10],\n}\n\n\nstatic func default_keys(shortcut :ShortCut) -> PackedInt32Array:\n\tmatch OS.get_name().to_lower():\n\t\t'windows':\n\t\t\treturn DEFAULTS_WINDOWS[shortcut]\n\t\t'macos':\n\t\t\treturn DEFAULTS_MACOS[shortcut]\n\t\t_:\n\t\t\treturn DEFAULTS_WINDOWS[shortcut]\n"
  },
  {
    "path": "addons/gdUnit4/src/core/command/GdUnitShortcut.gd.uid",
    "content": "uid://tlwbwprvc14u\n"
  },
  {
    "path": "addons/gdUnit4/src/core/command/GdUnitShortcutAction.gd",
    "content": "class_name GdUnitShortcutAction\nextends RefCounted\n\n\nfunc _init(p_type :GdUnitShortcut.ShortCut, p_shortcut :Shortcut, p_command :String) -> void:\n\tassert(p_type != null, \"missing parameter 'type'\")\n\tassert(p_shortcut != null, \"missing parameter 'shortcut'\")\n\tassert(p_command != null, \"missing parameter 'command'\")\n\tself.type = p_type\n\tself.shortcut = p_shortcut\n\tself.command = p_command\n\n\nvar type: GdUnitShortcut.ShortCut:\n\tset(value):\n\t\ttype = value\n\tget:\n\t\treturn type\n\n\nvar shortcut: Shortcut:\n\tset(value):\n\t\tshortcut = value\n\tget:\n\t\treturn shortcut\n\n\nvar command: String:\n\tset(value):\n\t\tcommand = value\n\tget:\n\t\treturn command\n\n\nfunc _to_string() -> String:\n\treturn \"GdUnitShortcutAction: %s (%s) -> %s\" % [GdUnitShortcut.ShortCut.keys()[type], shortcut.get_as_text(), command]\n"
  },
  {
    "path": "addons/gdUnit4/src/core/command/GdUnitShortcutAction.gd.uid",
    "content": "uid://c04l7ol71n4bu\n"
  },
  {
    "path": "addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd",
    "content": "extends RefCounted\n\n\n# Caches all test indices for parameterized tests\nclass TestCaseIndicesCache:\n\tvar _cache := {}\n\n\tfunc _key(resource_path: String, test_name: String) -> StringName:\n\t\treturn &\"%s_%s\" % [resource_path, test_name]\n\n\n\tfunc contains_test_case(resource_path: String, test_name: String) -> bool:\n\t\treturn _cache.has(_key(resource_path, test_name))\n\n\n\tfunc validate(resource_path: String, test_name: String, indices: PackedStringArray) -> bool:\n\t\tvar cached_indicies: PackedStringArray = _cache[_key(resource_path, test_name)]\n\t\treturn GdArrayTools.has_same_content(cached_indicies, indices)\n\n\n\tfunc sync(resource_path: String, test_name: String, indices: PackedStringArray) -> void:\n\t\tif indices.is_empty():\n\t\t\t_cache[_key(resource_path, test_name)] = []\n\t\telse:\n\t\t\t_cache[_key(resource_path, test_name)] = indices\n\n# contains all tracked test suites where discovered since editor start\n# key : test suite resource_path\n# value: the list of discovered test case names\nvar _discover_cache := {}\n\nvar discovered_test_case_indices_cache := TestCaseIndicesCache.new()\n\n\nfunc _init() -> void:\n\t# Register for discovery events to sync the cache\n\t@warning_ignore(\"return_value_discarded\")\n\tGdUnitSignals.instance().gdunit_add_test_suite.connect(sync_cache)\n\n\nfunc sync_cache(dto: GdUnitTestSuiteDto) -> void:\n\tvar resource_path := ProjectSettings.localize_path(dto.path())\n\tvar discovered_test_cases: Array[String] = []\n\tfor test_case in dto.test_cases():\n\t\tdiscovered_test_cases.append(test_case.name())\n\t\tdiscovered_test_case_indices_cache.sync(resource_path, test_case.name(), test_case.test_case_names())\n\t_discover_cache[resource_path] = discovered_test_cases\n\n\nfunc discover(script: Script) -> void:\n\t# for cs scripts we need to recomplie before discover new tests\n\tif GdObjects.is_cs_script(script):\n\t\tawait rebuild_project(script)\n\n\tif GdObjects.is_test_suite(script):\n\t\t# a new test suite is discovered\n\t\tvar script_path := ProjectSettings.localize_path(script.resource_path)\n\t\tvar scanner := GdUnitTestSuiteScanner.new()\n\t\tvar test_suite := scanner._parse_test_suite(script)\n\t\tvar suite_name := test_suite.get_name()\n\n\t\tif not _discover_cache.has(script_path):\n\t\t\tvar dto :GdUnitTestSuiteDto = GdUnitTestSuiteDto.of(test_suite)\n\t\t\tGdUnitSignals.instance().gdunit_event.emit(GdUnitEventTestDiscoverTestSuiteAdded.new(script_path, suite_name, dto))\n\t\t\tsync_cache(dto)\n\t\t\ttest_suite.queue_free()\n\t\t\treturn\n\n\t\tvar discovered_test_cases :Array[String] = _discover_cache.get(script_path, [] as Array[String])\n\t\tvar script_test_cases := extract_test_functions(test_suite)\n\n\t\t# first detect removed/renamed tests\n\t\tvar tests_removed := PackedStringArray()\n\t\tfor test_case in discovered_test_cases:\n\t\t\tif not script_test_cases.has(test_case):\n\t\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t\ttests_removed.append(test_case)\n\t\t# second detect new added tests\n\t\tvar tests_added :Array[String] = []\n\t\tfor test_case in script_test_cases:\n\t\t\tif not discovered_test_cases.has(test_case):\n\t\t\t\ttests_added.append(test_case)\n\n\t\t# We need to scan for parameterized test because of possible test data changes\n\t\t# For more details look at https://github.com/MikeSchulze/gdUnit4/issues/592\n\t\tfor test_case_name in script_test_cases:\n\t\t\tif discovered_test_case_indices_cache.contains_test_case(script_path, test_case_name):\n\t\t\t\tvar test_case: _TestCase = test_suite.find_child(test_case_name, false, false)\n\t\t\t\tvar test_indices := test_case.test_case_names()\n\t\t\t\tif not discovered_test_case_indices_cache.validate(script_path, test_case_name, test_indices):\n\t\t\t\t\tif !tests_removed.has(test_case_name):\n\t\t\t\t\t\ttests_removed.append(test_case_name)\n\t\t\t\t\tif !tests_added.has(test_case_name):\n\t\t\t\t\t\ttests_added.append(test_case_name)\n\t\t\t\t\tdiscovered_test_case_indices_cache.sync(script_path, test_case_name, test_indices)\n\n\t\t# finally notify changes to the inspector\n\t\tif not tests_removed.is_empty() or not tests_added.is_empty():\n\t\t\t# emit deleted tests\n\t\t\tfor test_name in tests_removed:\n\t\t\t\tdiscovered_test_cases.erase(test_name)\n\t\t\t\tGdUnitSignals.instance().gdunit_event.emit(GdUnitEventTestDiscoverTestRemoved.new(script_path, suite_name, test_name))\n\n\t\t\t# emit new discovered tests\n\t\t\tfor test_name in tests_added:\n\t\t\t\tdiscovered_test_cases.append(test_name)\n\t\t\t\tvar test_case := test_suite.find_child(test_name, false, false)\n\t\t\t\tvar dto := GdUnitTestCaseDto.new()\n\t\t\t\tdto = dto.deserialize(dto.serialize(test_case))\n\t\t\t\tGdUnitSignals.instance().gdunit_event.emit(GdUnitEventTestDiscoverTestAdded.new(script_path, suite_name, dto))\n\t\t\t\t# if the parameterized test fresh added we need to sync the cache\n\t\t\t\tif not discovered_test_case_indices_cache.contains_test_case(script_path, test_name):\n\t\t\t\t\tdiscovered_test_case_indices_cache.sync(script_path, test_name, dto.test_case_names())\n\n\t\t\t# update the cache\n\t\t\t_discover_cache[script_path] = discovered_test_cases\n\t\t\ttest_suite.queue_free()\n\n\nfunc extract_test_functions(test_suite :Node) -> PackedStringArray:\n\treturn test_suite.get_children()\\\n\t\t.filter(func(child: Node) -> bool: return is_instance_of(child, _TestCase))\\\n\t\t.map(func (child: Node) -> String: return child.get_name())\n\n\nfunc is_paramaterized_test(test_suite :Node, test_case_name: String) -> bool:\n\treturn test_suite.get_children()\\\n\t\t.filter(func(child: Node) -> bool: return child.name == test_case_name)\\\n\t\t.any(func (test: _TestCase) -> bool: return test.is_parameterized())\n\n\n# do rebuild the entire project, there is actual no way to enforce the Godot engine itself to do this\nfunc rebuild_project(script: Script) -> void:\n\tvar class_path := ProjectSettings.globalize_path(script.resource_path)\n\tprint_rich(\"[color=CORNFLOWER_BLUE]GdUnitTestDiscoverGuard: CSharpScript change detected on: '%s' [/color]\" % class_path)\n\tvar scene_tree := Engine.get_main_loop() as SceneTree\n\tawait scene_tree.process_frame\n\n\tvar output := []\n\tvar exit_code := OS.execute(\"dotnet\", [\"--version\"], output)\n\tif exit_code == -1:\n\t\tprint_rich(\"[color=CORNFLOWER_BLUE]GdUnitTestDiscoverGuard:[/color] [color=RED]Rebuild the project failed.[/color]\")\n\t\tprint_rich(\"[color=CORNFLOWER_BLUE]GdUnitTestDiscoverGuard:[/color] [color=RED]Can't find installed `dotnet`! Please check your environment is setup correctly.[/color]\")\n\t\treturn\n\tprint_rich(\"[color=CORNFLOWER_BLUE]GdUnitTestDiscoverGuard:[/color] [color=DEEP_SKY_BLUE]Found dotnet v%s[/color]\" % output[0].strip_edges())\n\toutput.clear()\n\n\texit_code = OS.execute(\"dotnet\", [\"build\"], output)\n\tprint_rich(\"[color=CORNFLOWER_BLUE]GdUnitTestDiscoverGuard:[/color] [color=DEEP_SKY_BLUE]Rebuild the project ... [/color]\")\n\tfor out:Variant in output:\n\t\tprint_rich(\"[color=DEEP_SKY_BLUE] \t\t%s\" % out.strip_edges())\n\tawait scene_tree.process_frame\n"
  },
  {
    "path": "addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd.uid",
    "content": "uid://bij4y2rn8dykj\n"
  },
  {
    "path": "addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd",
    "content": "class_name GdUnitTestDiscoverer\nextends RefCounted\n\n\nstatic func run() -> void:\n\tprints(\"Running test discovery ..\")\n\tGdUnitSignals.instance().gdunit_event.emit(GdUnitEventTestDiscoverStart.new())\n\tawait (Engine.get_main_loop() as SceneTree).create_timer(.5).timeout\n\n\t# We run the test discovery in an extra thread so that the main thread is not blocked\n\tvar t:= Thread.new()\n\t@warning_ignore(\"return_value_discarded\")\n\tt.start(func () -> void:\n\t\tvar test_suite_directories :PackedStringArray = GdUnitCommandHandler.scan_all_test_directories(GdUnitSettings.test_root_folder())\n\t\tvar scanner := GdUnitTestSuiteScanner.new()\n\t\tvar _test_suites_to_process :Array[Node] = []\n\n\t\tfor test_suite_dir in test_suite_directories:\n\t\t\t_test_suites_to_process.append_array(scanner.scan(test_suite_dir))\n\n\t\t# Do sync the main thread before emit the discovered test suites to the inspector\n\t\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\t\tvar test_case_count :int = 0\n\t\tfor test_suite in _test_suites_to_process:\n\t\t\ttest_case_count += test_suite.get_child_count()\n\t\t\tvar ts_dto := GdUnitTestSuiteDto.of(test_suite)\n\t\t\tGdUnitSignals.instance().gdunit_add_test_suite.emit(ts_dto)\n\t\t\ttest_suite.free()\n\n\t\tprints(\"%d test suites discovered.\" % _test_suites_to_process.size())\n\t\tGdUnitSignals.instance().gdunit_event.emit(GdUnitEventTestDiscoverEnd.new(_test_suites_to_process.size(), test_case_count))\n\t\t_test_suites_to_process.clear()\n\t)\n\t# wait unblocked to the tread is finished\n\twhile t.is_alive():\n\t\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\t# needs finally to wait for finish\n\tawait t.wait_to_finish()\n"
  },
  {
    "path": "addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd.uid",
    "content": "uid://bgv52sic586h3\n"
  },
  {
    "path": "addons/gdUnit4/src/core/event/GdUnitEvent.gd",
    "content": "class_name GdUnitEvent\nextends Resource\n\nconst WARNINGS = \"warnings\"\nconst FAILED = \"failed\"\nconst FLAKY = \"flaky\"\nconst ERRORS = \"errors\"\nconst SKIPPED = \"skipped\"\nconst ELAPSED_TIME = \"elapsed_time\"\nconst ORPHAN_NODES = \"orphan_nodes\"\nconst ERROR_COUNT = \"error_count\"\nconst FAILED_COUNT = \"failed_count\"\nconst SKIPPED_COUNT = \"skipped_count\"\nconst RETRY_COUNT = \"retry_count\"\n\nenum {\n\tINIT,\n\tSTOP,\n\tTESTSUITE_BEFORE,\n\tTESTSUITE_AFTER,\n\tTESTCASE_BEFORE,\n\tTESTCASE_AFTER,\n\tTESTCASE_STATISTICS,\n\tDISCOVER_START,\n\tDISCOVER_END,\n\tDISCOVER_SUITE_ADDED,\n\tDISCOVER_TEST_ADDED,\n\tDISCOVER_TEST_REMOVED,\n}\n\nvar _event_type :int\nvar _resource_path :String\nvar _suite_name :String\nvar _test_name :String\nvar _total_count :int = 0\nvar _statistics := Dictionary()\nvar _reports :Array[GdUnitReport] = []\n\n\nfunc suite_before(p_resource_path :String, p_suite_name :String, p_total_count :int) -> GdUnitEvent:\n\t_event_type = TESTSUITE_BEFORE\n\t_resource_path = p_resource_path\n\t_suite_name = p_suite_name\n\t_test_name = \"before\"\n\t_total_count = p_total_count\n\treturn self\n\n\nfunc suite_after(p_resource_path :String, p_suite_name :String, p_statistics :Dictionary = {}, p_reports :Array[GdUnitReport] = []) -> GdUnitEvent:\n\t_event_type = TESTSUITE_AFTER\n\t_resource_path = p_resource_path\n\t_suite_name  = p_suite_name\n\t_test_name = \"after\"\n\t_statistics = p_statistics\n\t_reports = p_reports\n\treturn self\n\n\nfunc test_before(p_resource_path :String, p_suite_name :String, p_test_name :String) -> GdUnitEvent:\n\t_event_type = TESTCASE_BEFORE\n\t_resource_path = p_resource_path\n\t_suite_name  = p_suite_name\n\t_test_name = p_test_name\n\treturn self\n\n\nfunc test_after(p_resource_path :String, p_suite_name :String, p_test_name :String, p_statistics :Dictionary = {}, p_reports :Array[GdUnitReport] = []) -> GdUnitEvent:\n\t_event_type = TESTCASE_AFTER\n\t_resource_path = p_resource_path\n\t_suite_name  = p_suite_name\n\t_test_name = p_test_name\n\t_statistics = p_statistics\n\t_reports = p_reports\n\treturn self\n\n\nfunc test_statistics(p_resource_path :String, p_suite_name :String, p_test_name :String, p_statistics :Dictionary = {}) -> GdUnitEvent:\n\t_event_type = TESTCASE_STATISTICS\n\t_resource_path = p_resource_path\n\t_suite_name  = p_suite_name\n\t_test_name = p_test_name\n\t_statistics = p_statistics\n\treturn self\n\n\nfunc type() -> int:\n\treturn _event_type\n\n\nfunc suite_name() -> String:\n\treturn _suite_name\n\n\nfunc test_name() -> String:\n\treturn _test_name\n\n\nfunc elapsed_time() -> int:\n\treturn _statistics.get(ELAPSED_TIME, 0)\n\n\nfunc orphan_nodes() -> int:\n\treturn  _statistics.get(ORPHAN_NODES, 0)\n\n\nfunc statistic(p_type :String) -> int:\n\treturn _statistics.get(p_type, 0)\n\n\nfunc total_count() -> int:\n\treturn _total_count\n\n\nfunc success_count() -> int:\n\treturn total_count() - error_count() - failed_count() - skipped_count()\n\n\nfunc error_count() -> int:\n\treturn _statistics.get(ERROR_COUNT, 0)\n\n\nfunc failed_count() -> int:\n\treturn _statistics.get(FAILED_COUNT, 0)\n\n\nfunc skipped_count() -> int:\n\treturn _statistics.get(SKIPPED_COUNT, 0)\n\n\nfunc resource_path() -> String:\n\treturn _resource_path\n\n\nfunc is_success() -> bool:\n\treturn not is_warning() and not is_failed() and not is_error() and not is_skipped()\n\n\nfunc is_warning() -> bool:\n\treturn _statistics.get(WARNINGS, false)\n\n\nfunc is_failed() -> bool:\n\treturn _statistics.get(FAILED, false)\n\n\nfunc is_error() -> bool:\n\treturn _statistics.get(ERRORS, false)\n\n\nfunc is_flaky() -> bool:\n\treturn _statistics.get(FLAKY, false)\n\n\nfunc is_skipped() -> bool:\n\treturn _statistics.get(SKIPPED, false)\n\n\nfunc reports() -> Array[GdUnitReport]:\n\treturn _reports\n\n\nfunc _to_string() -> String:\n\treturn \"Event: %s %s:%s, %s, %s\" % [_event_type, _suite_name, _test_name, _statistics, _reports]\n\n\nfunc serialize() -> Dictionary:\n\tvar serialized := {\n\t\t\"type\"         : _event_type,\n\t\t\"resource_path\": _resource_path,\n\t\t\"suite_name\"   : _suite_name,\n\t\t\"test_name\"    : _test_name,\n\t\t\"total_count\"  : _total_count,\n\t\t\"statistics\"    : _statistics\n\t}\n\tserialized[\"reports\"] = _serialize_TestReports()\n\treturn serialized\n\n\nfunc deserialize(serialized :Dictionary) -> GdUnitEvent:\n\t_event_type    = serialized.get(\"type\", null)\n\t_resource_path = serialized.get(\"resource_path\", null)\n\t_suite_name    = serialized.get(\"suite_name\", null)\n\t_test_name     = serialized.get(\"test_name\", \"unknown\")\n\t_total_count   = serialized.get(\"total_count\", 0)\n\t_statistics    = serialized.get(\"statistics\", Dictionary())\n\tif serialized.has(\"reports\"):\n\t\t# needs this workaround to copy typed values in the array\n\t\tvar reports_to_deserializ :Array[Dictionary] = []\n\t\t@warning_ignore(\"unsafe_cast\")\n\t\treports_to_deserializ.append_array(serialized.get(\"reports\") as Array)\n\t\t_reports = _deserialize_reports(reports_to_deserializ)\n\treturn self\n\n\nfunc _serialize_TestReports() -> Array[Dictionary]:\n\tvar serialized_reports :Array[Dictionary] = []\n\tfor report in _reports:\n\t\tserialized_reports.append(report.serialize())\n\treturn serialized_reports\n\n\nfunc _deserialize_reports(p_reports :Array[Dictionary]) -> Array[GdUnitReport]:\n\tvar deserialized_reports :Array[GdUnitReport] = []\n\tfor report in p_reports:\n\t\tvar test_report := GdUnitReport.new().deserialize(report)\n\t\tdeserialized_reports.append(test_report)\n\treturn deserialized_reports\n"
  },
  {
    "path": "addons/gdUnit4/src/core/event/GdUnitEvent.gd.uid",
    "content": "uid://dtevq4bpw7wu7\n"
  },
  {
    "path": "addons/gdUnit4/src/core/event/GdUnitEventInit.gd",
    "content": "class_name GdUnitInit\nextends GdUnitEvent\n\n\nvar _total_testsuites :int\n\n\nfunc _init(p_total_testsuites :int, p_total_count :int) -> void:\n\t_event_type = INIT\n\t_total_testsuites = p_total_testsuites\n\t_total_count = p_total_count\n\n\nfunc total_test_suites() -> int:\n\treturn _total_testsuites\n\n\nfunc total_tests() -> int:\n\treturn _total_count\n"
  },
  {
    "path": "addons/gdUnit4/src/core/event/GdUnitEventInit.gd.uid",
    "content": "uid://ne3d7ha8m6r\n"
  },
  {
    "path": "addons/gdUnit4/src/core/event/GdUnitEventStop.gd",
    "content": "class_name GdUnitStop\nextends GdUnitEvent\n\n\nfunc _init() -> void:\n\t_event_type = STOP\n"
  },
  {
    "path": "addons/gdUnit4/src/core/event/GdUnitEventStop.gd.uid",
    "content": "uid://dalj3bau3dnu7\n"
  },
  {
    "path": "addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverEnd.gd",
    "content": "class_name GdUnitEventTestDiscoverEnd\nextends GdUnitEvent\n\n\nvar _total_testsuites: int\n\n\nfunc _init(testsuite_count: int, test_count: int) -> void:\n\t_event_type = DISCOVER_END\n\t_total_testsuites = testsuite_count\n\t_total_count = test_count\n\n\nfunc total_test_suites() -> int:\n\treturn _total_testsuites\n\n\nfunc total_tests() -> int:\n\treturn _total_count\n"
  },
  {
    "path": "addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverEnd.gd.uid",
    "content": "uid://co2w6mhr7n2q7\n"
  },
  {
    "path": "addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverStart.gd",
    "content": "class_name GdUnitEventTestDiscoverStart\nextends GdUnitEvent\n\n\nfunc _init() -> void:\n\t_event_type = DISCOVER_START\n"
  },
  {
    "path": "addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverStart.gd.uid",
    "content": "uid://cxtnuqbkh82bq\n"
  },
  {
    "path": "addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverTestAdded.gd",
    "content": "class_name GdUnitEventTestDiscoverTestAdded\nextends GdUnitEvent\n\n\nvar _test_case_dto: GdUnitTestCaseDto\n\n\nfunc _init(arg_resource_path: String, arg_suite_name: String, arg_test_case_dto: GdUnitTestCaseDto) -> void:\n\t_event_type = DISCOVER_TEST_ADDED\n\t_resource_path = arg_resource_path\n\t_suite_name  = arg_suite_name\n\t_test_name = arg_test_case_dto.name()\n\t_test_case_dto = arg_test_case_dto\n\n\nfunc test_case_dto() -> GdUnitTestCaseDto:\n\treturn _test_case_dto\n"
  },
  {
    "path": "addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverTestAdded.gd.uid",
    "content": "uid://cclg1o6gfxxun\n"
  },
  {
    "path": "addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverTestRemoved.gd",
    "content": "class_name GdUnitEventTestDiscoverTestRemoved\nextends GdUnitEvent\n\n\nfunc _init(arg_resource_path: String, arg_suite_name: String, arg_test_name: String) -> void:\n\t_event_type = DISCOVER_TEST_REMOVED\n\t_resource_path = arg_resource_path\n\t_suite_name  = arg_suite_name\n\t_test_name = arg_test_name\n"
  },
  {
    "path": "addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverTestRemoved.gd.uid",
    "content": "uid://c7hmlrbo0kcxe\n"
  },
  {
    "path": "addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverTestSuiteAdded.gd",
    "content": "class_name GdUnitEventTestDiscoverTestSuiteAdded\nextends GdUnitEvent\n\n\nvar _dto: GdUnitTestSuiteDto\n\n\nfunc _init(arg_resource_path: String, arg_suite_name: String, arg_dto: GdUnitTestSuiteDto) -> void:\n\t_event_type = DISCOVER_SUITE_ADDED\n\t_resource_path = arg_resource_path\n\t_suite_name  = arg_suite_name\n\t_dto = arg_dto\n\n\nfunc suite_dto() -> GdUnitTestSuiteDto:\n\treturn _dto\n"
  },
  {
    "path": "addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverTestSuiteAdded.gd.uid",
    "content": "uid://468tg73hhbmu\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd",
    "content": "## The execution context\n## It contains all the necessary information about the executed stage, such as memory observers, reports, orphan monitor\nclass_name GdUnitExecutionContext\n\nvar _parent_context: GdUnitExecutionContext\nvar _sub_context: Array[GdUnitExecutionContext] = []\nvar _orphan_monitor: GdUnitOrphanNodesMonitor\nvar _memory_observer: GdUnitMemoryObserver\nvar _report_collector: GdUnitTestReportCollector\nvar _timer: LocalTime\nvar _test_case_name: StringName\nvar _test_case_parameter_set: Array\nvar _name: String\nvar _test_execution_iteration: int = 0\nvar _flaky_test_check := GdUnitSettings.is_test_flaky_check_enabled()\nvar _flaky_test_retries := GdUnitSettings.get_flaky_max_retries()\n\n\n# execution states\nvar _is_calculated := false\nvar _is_success: bool\nvar _is_flaky: bool\nvar _is_skipped: bool\nvar _has_warnings: bool\nvar _has_failures: bool\nvar _has_errors: bool\nvar _failure_count := 0\nvar _orphan_count := 0\nvar _error_count := 0\nvar _skipped_count := 0\n\n\nvar error_monitor: GodotGdErrorMonitor = null:\n\tget:\n\t\tif _parent_context != null:\n\t\t\treturn _parent_context.error_monitor\n\t\tif error_monitor == null:\n\t\t\terror_monitor = GodotGdErrorMonitor.new()\n\t\treturn error_monitor\n\n\nvar test_suite: GdUnitTestSuite = null:\n\tget:\n\t\tif _parent_context != null:\n\t\t\treturn _parent_context.test_suite\n\t\treturn test_suite\n\n\nvar test_case: _TestCase = null:\n\tget:\n\t\tif test_case == null and _parent_context != null:\n\t\t\treturn _parent_context.test_case\n\t\treturn test_case\n\n\nfunc _init(name: StringName, parent_context: GdUnitExecutionContext = null) -> void:\n\t_name = name\n\t_parent_context = parent_context\n\t_timer = LocalTime.now()\n\t_orphan_monitor = GdUnitOrphanNodesMonitor.new(name)\n\t_orphan_monitor.start()\n\t_memory_observer = GdUnitMemoryObserver.new()\n\t_report_collector = GdUnitTestReportCollector.new()\n\tif parent_context != null:\n\t\tparent_context._sub_context.append(self)\n\n\nfunc dispose() -> void:\n\t_timer = null\n\t_orphan_monitor = null\n\t_report_collector = null\n\t_memory_observer = null\n\t_parent_context = null\n\ttest_suite = null\n\ttest_case = null\n\tdispose_sub_contexts()\n\n\nfunc dispose_sub_contexts() -> void:\n\tfor context in _sub_context:\n\t\tcontext.dispose()\n\t_sub_context.clear()\n\n\nstatic func of(pe: GdUnitExecutionContext) -> GdUnitExecutionContext:\n\tvar context := GdUnitExecutionContext.new(pe._test_case_name, pe)\n\tcontext._test_case_name = pe._test_case_name\n\tcontext._test_execution_iteration = pe._test_execution_iteration\n\treturn context\n\n\nstatic func of_test_suite(p_test_suite: GdUnitTestSuite) -> GdUnitExecutionContext:\n\tassert(p_test_suite, \"test_suite is null\")\n\tvar context := GdUnitExecutionContext.new(p_test_suite.get_name())\n\tcontext.test_suite = p_test_suite\n\treturn context\n\n\nstatic func of_test_case(pe: GdUnitExecutionContext, p_test_case: _TestCase) -> GdUnitExecutionContext:\n\tassert(p_test_case, \"test_case is null\")\n\tvar context := GdUnitExecutionContext.new(p_test_case.get_name(), pe)\n\tcontext.test_case = p_test_case\n\treturn context\n\n\nstatic func of_parameterized_test(pe: GdUnitExecutionContext, test_case_name: String, test_case_parameter_set: Array) -> GdUnitExecutionContext:\n\tvar context := GdUnitExecutionContext.new(test_case_name, pe)\n\tcontext._test_case_name = test_case_name\n\tcontext._test_case_parameter_set = test_case_parameter_set\n\treturn context\n\n\nfunc get_test_suite_path() -> String:\n\treturn test_suite.get_script().resource_path\n\n\nfunc get_test_suite_name() -> StringName:\n\treturn test_suite.get_name()\n\n\nfunc get_test_case_name() -> StringName:\n\tif _test_case_name.is_empty():\n\t\treturn test_case.get_name()\n\treturn _test_case_name\n\n\nfunc error_monitor_start() -> void:\n\terror_monitor.start()\n\n\nfunc error_monitor_stop() -> void:\n\tawait error_monitor.scan()\n\tfor error_report in error_monitor.to_reports():\n\t\tif error_report.is_error():\n\t\t\t_report_collector.push_back(error_report)\n\n\nfunc orphan_monitor_start() -> void:\n\t_orphan_monitor.start()\n\n\nfunc orphan_monitor_stop() -> void:\n\t_orphan_monitor.stop()\n\n\nfunc add_report(report: GdUnitReport) -> void:\n\t_report_collector.push_back(report)\n\n\nfunc reports() -> Array[GdUnitReport]:\n\treturn _report_collector.reports()\n\n\nfunc collect_reports(recursive: bool) -> Array[GdUnitReport]:\n\tif not recursive:\n\t\treturn reports()\n\tvar current_reports := reports()\n\t# we combine the reports of test_before(), test_after() and test() to be reported by `fire_test_ended`\n\tfor sub_context in _sub_context:\n\t\tcurrent_reports.append_array(sub_context.reports())\n\t\t# needs finally to clean the test reports to avoid counting twice\n\t\tsub_context.reports().clear()\n\treturn current_reports\n\n\nfunc collect_orphans(p_reports: Array[GdUnitReport]) -> int:\n\tvar orphans := 0\n\tif not _sub_context.is_empty():\n\t\torphans += collect_testcase_orphan_reports(_sub_context[0], p_reports)\n\torphans += collect_teststage_orphan_reports(p_reports)\n\treturn orphans\n\n\nfunc collect_testcase_orphan_reports(context: GdUnitExecutionContext, p_reports: Array[GdUnitReport]) -> int:\n\tvar orphans := context.count_orphans()\n\tif orphans > 0:\n\t\tp_reports.push_front(GdUnitReport.new()\\\n\t\t\t.create(GdUnitReport.WARN, context.test_case.line_number(), GdAssertMessages.orphan_detected_on_test(orphans)))\n\treturn orphans\n\n\nfunc collect_teststage_orphan_reports(p_reports: Array[GdUnitReport]) -> int:\n\tvar orphans := count_orphans()\n\tif orphans > 0:\n\t\tp_reports.push_front(GdUnitReport.new()\\\n\t\t\t.create(GdUnitReport.WARN, test_case.line_number(), GdAssertMessages.orphan_detected_on_test_setup(orphans)))\n\treturn orphans\n\n\nfunc build_reports(recursive:= true) -> Array[GdUnitReport]:\n\tvar collected_reports: Array[GdUnitReport] = collect_reports(recursive)\n\tif recursive:\n\t\t_orphan_count = collect_orphans(collected_reports)\n\telse:\n\t\t_orphan_count = count_orphans()\n\t\tif _orphan_count > 0:\n\t\t\tcollected_reports.push_front(GdUnitReport.new() \\\n\t\t\t\t.create(GdUnitReport.WARN, 1, GdAssertMessages.orphan_detected_on_suite_setup(_orphan_count)))\n\t_is_skipped = is_skipped()\n\t_skipped_count = count_skipped(recursive)\n\t_is_success = is_success()\n\t_is_flaky = is_flaky()\n\t_has_warnings = has_warnings()\n\t_has_errors = has_errors()\n\t_error_count = count_errors(recursive)\n\tif !_is_success:\n\t\t_has_failures = has_failures()\n\t\t_failure_count = count_failures(recursive)\n\t_is_calculated = true\n\treturn collected_reports\n\n\n# Evaluates the actual test case status by validate latest execution state (cold be more based on flaky max retry count)\nfunc evaluate_test_retry_status() -> bool:\n\t# get latest test execution status\n\tvar last_test_status :GdUnitExecutionContext = _sub_context.back()\n\t_is_skipped = last_test_status.is_skipped()\n\t_skipped_count = last_test_status.count_skipped(false)\n\t_is_success = last_test_status.is_success()\n\t# if success but it have more than one sub contexts the test was rerurn becouse of failures and will be marked as flaky\n\t_is_flaky = _is_success and _sub_context.size() > 1\n\t_has_warnings = last_test_status.has_warnings()\n\t_has_errors = last_test_status.has_errors()\n\t_error_count = last_test_status.count_errors(false)\n\t_has_failures = last_test_status.has_failures()\n\t_failure_count = last_test_status.count_failures(false)\n\t_orphan_count = last_test_status.collect_orphans(collect_reports(false))\n\t_is_calculated = true\n\t# finally cleanup the retry execution contexts\n\tdispose_sub_contexts()\n\treturn _is_success\n\n\nfunc get_execution_statistics() -> Dictionary:\n\treturn {\n\t\tGdUnitEvent.RETRY_COUNT: _test_execution_iteration,\n\t\tGdUnitEvent.ORPHAN_NODES: _orphan_count,\n\t\tGdUnitEvent.ELAPSED_TIME: _timer.elapsed_since_ms(),\n\t\tGdUnitEvent.FAILED: !_is_success,\n\t\tGdUnitEvent.ERRORS: _has_errors,\n\t\tGdUnitEvent.WARNINGS: _has_warnings,\n\t\tGdUnitEvent.FLAKY: _is_flaky,\n\t\tGdUnitEvent.SKIPPED: _is_skipped,\n\t\tGdUnitEvent.FAILED_COUNT: _failure_count,\n\t\tGdUnitEvent.ERROR_COUNT: _error_count,\n\t\tGdUnitEvent.SKIPPED_COUNT: _skipped_count\n\t}\n\n\nfunc has_failures() -> bool:\n\treturn (\n\t\t_sub_context.any(func(c :GdUnitExecutionContext) -> bool:\n\t\t\treturn c._has_failures if c._is_calculated else c.has_failures())\n\t\tor _report_collector.has_failures()\n\t)\n\n\nfunc has_errors() -> bool:\n\treturn (\n\t\t_sub_context.any(func(c :GdUnitExecutionContext) -> bool:\n\t\t\treturn c._has_errors if c._is_calculated else c.has_errors())\n\t\tor _report_collector.has_errors()\n\t)\n\n\nfunc has_warnings() -> bool:\n\treturn (\n\t\t_sub_context.any(func(c :GdUnitExecutionContext) -> bool:\n\t\t\treturn c._has_warnings if c._is_calculated else c.has_warnings())\n\t\tor _report_collector.has_warnings()\n\t)\n\n\nfunc is_flaky() -> bool:\n\treturn (\n\t\t_sub_context.any(func(c :GdUnitExecutionContext) -> bool:\n\t\t\treturn c._is_flaky if c._is_calculated else c.is_flaky())\n\t\tor _test_execution_iteration > 1\n\t)\n\n\nfunc is_success() -> bool:\n\tif _sub_context.is_empty():\n\t\treturn not has_failures()\n\n\tvar failed_context := _sub_context.filter(func(c :GdUnitExecutionContext) -> bool:\n\t\t\treturn !(c._is_success if c._is_calculated else c.is_success()))\n\treturn failed_context.is_empty() and not has_failures()\n\n\nfunc is_skipped() -> bool:\n\treturn (\n\t\t_sub_context.any(func(c :GdUnitExecutionContext) -> bool:\n\t\t\treturn c._is_skipped if c._is_calculated else c.is_skipped())\n\t\tor test_case.is_skipped() if test_case != null else false\n\t)\n\n\nfunc is_interupted() -> bool:\n\treturn false if test_case == null else test_case.is_interupted()\n\n\nfunc count_failures(recursive: bool) -> int:\n\tif not recursive:\n\t\treturn _report_collector.count_failures()\n\treturn _sub_context\\\n\t\t.map(func(c :GdUnitExecutionContext) -> int:\n\t\t\t\treturn c.count_failures(recursive)).reduce(sum, _report_collector.count_failures())\n\n\nfunc count_errors(recursive: bool) -> int:\n\tif not recursive:\n\t\treturn _report_collector.count_errors()\n\treturn _sub_context\\\n\t\t.map(func(c :GdUnitExecutionContext) -> int:\n\t\t\t\treturn c.count_errors(recursive)).reduce(sum, _report_collector.count_errors())\n\n\nfunc count_skipped(recursive: bool) -> int:\n\tif not recursive:\n\t\treturn _report_collector.count_skipped()\n\treturn _sub_context\\\n\t\t.map(func(c :GdUnitExecutionContext) -> int:\n\t\t\t\treturn c.count_skipped(recursive)).reduce(sum, _report_collector.count_skipped())\n\n\nfunc count_orphans() -> int:\n\tvar orphans := 0\n\tfor c in _sub_context:\n\t\torphans += c._orphan_monitor.orphan_nodes()\n\treturn _orphan_monitor.orphan_nodes() - orphans\n\n\nfunc sum(accum: int, number: int) -> int:\n\treturn accum + number\n\n\nfunc retry_execution() -> bool:\n\tvar retry :=  _test_execution_iteration < 1 if not _flaky_test_check else _test_execution_iteration < _flaky_test_retries\n\tif retry:\n\t\t_test_execution_iteration += 1\n\treturn retry\n\n\nfunc register_auto_free(obj: Variant) -> Variant:\n\treturn _memory_observer.register_auto_free(obj)\n\n\n## Runs the gdunit garbage collector to free registered object\nfunc gc() -> void:\n\t# unreference last used assert form the test to prevent memory leaks\n\tGdUnitThreadManager.get_current_context().clear_assert()\n\tawait _memory_observer.gc()\n\torphan_monitor_stop()\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd.uid",
    "content": "uid://b7xmq1h8md1tt\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/GdUnitMemoryObserver.gd",
    "content": "## The memory watcher for objects that have been registered and are released when 'gc' is called.\nclass_name GdUnitMemoryObserver\nextends RefCounted\n\nconst TAG_OBSERVE_INSTANCE := \"GdUnit4_observe_instance_\"\nconst TAG_AUTO_FREE = \"GdUnit4_marked_auto_free\"\nconst GdUnitTools = preload(\"res://addons/gdUnit4/src/core/GdUnitTools.gd\")\n\n\nvar _store :Array[Variant] = []\n# enable for debugging purposes\nvar _is_stdout_verbose := false\nconst _show_debug := false\n\n\n## Registration of an instance to be released when an execution phase is completed\nfunc register_auto_free(obj :Variant) -> Variant:\n\tif not is_instance_valid(obj):\n\t\treturn obj\n\t# do not register on GDScriptNativeClass\n\t@warning_ignore(\"unsafe_cast\")\n\tif typeof(obj) == TYPE_OBJECT and (obj as Object).is_class(\"GDScriptNativeClass\") :\n\t\treturn obj\n\t#if obj is GDScript or obj is ScriptExtension:\n\t#\treturn obj\n\tif obj is MainLoop:\n\t\tpush_error(\"GdUnit4: Avoid to add mainloop to auto_free queue  %s\" % obj)\n\t\treturn\n\tif _is_stdout_verbose:\n\t\tprint_verbose(\"GdUnit4:gc():register auto_free(%s)\" % obj)\n\t# only register pure objects\n\tif obj is GdUnitSceneRunner:\n\t\t_store.push_back(obj)\n\telse:\n\t\t_store.append(obj)\n\t_tag_object(obj)\n\treturn obj\n\n\n# to disable instance guard when run into issues.\nstatic func _is_instance_guard_enabled() -> bool:\n\treturn false\n\n\n@warning_ignore(\"unsafe_method_access\")\nstatic func debug_observe(name :String, obj :Object, indent :int = 0) -> void:\n\tif not _show_debug:\n\t\treturn\n\tvar script :GDScript= obj if obj is GDScript else obj.get_script()\n\tif script:\n\t\tvar base_script :GDScript = script.get_base_script()\n\t\tprints(\"\".lpad(indent, \"\t\"), name, obj, obj.get_class(), \"reference_count:\", obj.get_reference_count() if obj is RefCounted else 0, \"script:\", script, script.resource_path)\n\t\tif base_script:\n\t\t\tdebug_observe(\"+\", base_script, indent+1)\n\telse:\n\t\tprints(name, obj, obj.get_class(), obj.get_name())\n\n\nstatic func guard_instance(obj :Object) -> void:\n\tif not _is_instance_guard_enabled():\n\t\treturn\n\tvar tag := TAG_OBSERVE_INSTANCE + str(abs(obj.get_instance_id()))\n\tif Engine.has_meta(tag):\n\t\treturn\n\tdebug_observe(\"Gard on instance\", obj)\n\tEngine.set_meta(tag, obj)\n\n\nstatic func unguard_instance(obj :Object, verbose := true) -> void:\n\tif not _is_instance_guard_enabled():\n\t\treturn\n\tvar tag := TAG_OBSERVE_INSTANCE + str(abs(obj.get_instance_id()))\n\tif verbose:\n\t\tdebug_observe(\"unguard instance\", obj)\n\tif Engine.has_meta(tag):\n\t\tEngine.remove_meta(tag)\n\n\nstatic func gc_guarded_instance(name :String, instance :Object) -> void:\n\tif not _is_instance_guard_enabled():\n\t\treturn\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\tunguard_instance(instance, false)\n\tif is_instance_valid(instance) and instance is RefCounted:\n\t\t# finally do this very hacky stuff\n\t\t# we need to manually unreferece to avoid leaked scripts\n\t\t# but still leaked GDScriptFunctionState exists\n\t\t#var script :GDScript = instance.get_script()\n\t\t#if script:\n\t\t#\tvar base_script :GDScript = script.get_base_script()\n\t\t#\tif base_script:\n\t\t#\t\tbase_script.unreference()\n\t\tdebug_observe(name, instance)\n\t\t(instance as RefCounted).unreference()\n\t\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\n\nstatic func gc_on_guarded_instances() -> void:\n\tif not _is_instance_guard_enabled():\n\t\treturn\n\tfor tag in Engine.get_meta_list():\n\t\tif tag.begins_with(TAG_OBSERVE_INSTANCE):\n\t\t\tvar instance :Object = Engine.get_meta(tag)\n\t\t\tawait gc_guarded_instance(\"Leaked instance detected:\", instance)\n\t\t\tawait GdUnitTools.free_instance(instance, false)\n\n\n# store the object into global store aswell to be verified by 'is_marked_auto_free'\nfunc _tag_object(obj :Variant) -> void:\n\tvar tagged_object: Array = Engine.get_meta(TAG_AUTO_FREE, [])\n\ttagged_object.append(obj)\n\tEngine.set_meta(TAG_AUTO_FREE, tagged_object)\n\n\n## Runs over all registered objects and releases them\nfunc gc() -> void:\n\tif _store.is_empty():\n\t\treturn\n\t# give engine time to free objects to process objects marked by queue_free()\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\tif _is_stdout_verbose:\n\t\tprint_verbose(\"GdUnit4:gc():running\", \" freeing %d objects ..\" % _store.size())\n\tvar tagged_objects: Array = Engine.get_meta(TAG_AUTO_FREE, [])\n\twhile not _store.is_empty():\n\t\tvar value :Variant = _store.pop_front()\n\t\ttagged_objects.erase(value)\n\t\tawait GdUnitTools.free_instance(value, _is_stdout_verbose)\n\tassert(_store.is_empty(), \"The memory observer has still entries in the store!\")\n\n\n## Checks whether the specified object is registered for automatic release\nstatic func is_marked_auto_free(obj: Variant) -> bool:\n\tvar tagged_objects: Array = Engine.get_meta(TAG_AUTO_FREE, [])\n\treturn tagged_objects.has(obj)\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/GdUnitMemoryObserver.gd.uid",
    "content": "uid://bjh5d0j7jrdiw\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd",
    "content": "# Collects all reports seperated as warnings, failures and errors\nclass_name GdUnitTestReportCollector\nextends RefCounted\n\n\nvar _reports :Array[GdUnitReport] = []\n\n\nstatic func __filter_is_error(report :GdUnitReport) -> bool:\n\treturn report.is_error()\n\n\nstatic func __filter_is_failure(report :GdUnitReport) -> bool:\n\treturn report.is_failure()\n\n\nstatic func __filter_is_warning(report :GdUnitReport) -> bool:\n\treturn report.is_warning()\n\n\nstatic func __filter_is_skipped(report :GdUnitReport) -> bool:\n\treturn report.is_skipped()\n\n\nfunc count_failures() -> int:\n\treturn _reports.filter(__filter_is_failure).size()\n\n\nfunc count_errors() -> int:\n\treturn _reports.filter(__filter_is_error).size()\n\n\nfunc count_warnings() -> int:\n\treturn _reports.filter(__filter_is_warning).size()\n\n\nfunc count_skipped() -> int:\n\treturn _reports.filter(__filter_is_skipped).size()\n\n\nfunc has_failures() -> bool:\n\treturn _reports.any(__filter_is_failure)\n\n\nfunc has_errors() -> bool:\n\treturn _reports.any(__filter_is_error)\n\n\nfunc has_warnings() -> bool:\n\treturn _reports.any(__filter_is_warning)\n\n\nfunc has_skipped() -> bool:\n\treturn _reports.any(__filter_is_skipped)\n\n\nfunc reports() -> Array[GdUnitReport]:\n\treturn _reports\n\n\nfunc push_back(report :GdUnitReport) -> void:\n\t_reports.push_back(report)\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd.uid",
    "content": "uid://ckxj7uegn6ccd\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd",
    "content": "## The executor to run a test-suite\nclass_name GdUnitTestSuiteExecutor\n\n\n# preload all asserts here\n@warning_ignore(\"unused_private_class_variable\")\nvar _assertions := GdUnitAssertions.new()\nvar _executeStage := GdUnitTestSuiteExecutionStage.new()\n\n\nfunc _init(debug_mode :bool = false) -> void:\n\t_executeStage.set_debug_mode(debug_mode)\n\n\nfunc execute(test_suite :GdUnitTestSuite) -> void:\n\tvar orphan_detection_enabled := GdUnitSettings.is_verbose_orphans()\n\tif not orphan_detection_enabled:\n\t\tprints(\"!!! Reporting orphan nodes is disabled. Please check GdUnit settings.\")\n\n\t(Engine.get_main_loop() as SceneTree).root.call_deferred(\"add_child\", test_suite)\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\tawait _executeStage.execute(GdUnitExecutionContext.of_test_suite(test_suite))\n\n\nfunc fail_fast(enabled :bool) -> void:\n\t_executeStage.fail_fast(enabled)\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd.uid",
    "content": "uid://xprnvxgmvamj\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd",
    "content": "## The test case shutdown hook implementation.[br]\n## It executes the 'test_after()' block from the test-suite.\nclass_name GdUnitTestCaseAfterStage\nextends IGdUnitExecutionStage\n\n\nvar _call_stage: bool\n\n\nfunc _init(call_stage := true) -> void:\n\t_call_stage = call_stage\n\n\nfunc _execute(context: GdUnitExecutionContext) -> void:\n\tvar test_suite := context.test_suite\n\n\tif _call_stage:\n\t\t@warning_ignore(\"redundant_await\")\n\t\tawait test_suite.after_test()\n\n\tawait context.gc()\n\tawait context.error_monitor_stop()\n\n\tvar reports := context.build_reports()\n\n\tif context.is_skipped():\n\t\tfire_test_skipped(context)\n\telse:\n\t\tfire_event(GdUnitEvent.new() \\\n\t\t\t.test_after(context.get_test_suite_path(),\n\t\t\t\tcontext.get_test_suite_name(),\n\t\t\t\tcontext.get_test_case_name(),\n\t\t\t\tcontext.get_execution_statistics(),\n\t\t\t\treports))\n\n\nfunc fire_test_skipped(context: GdUnitExecutionContext) -> void:\n\tvar test_case := context.test_case\n\tvar statistics := {\n\t\tGdUnitEvent.ORPHAN_NODES: 0,\n\t\tGdUnitEvent.ELAPSED_TIME: 0,\n\t\tGdUnitEvent.WARNINGS: false,\n\t\tGdUnitEvent.ERRORS: false,\n\t\tGdUnitEvent.ERROR_COUNT: 0,\n\t\tGdUnitEvent.FAILED: false,\n\t\tGdUnitEvent.FAILED_COUNT: 0,\n\t\tGdUnitEvent.SKIPPED: true,\n\t\tGdUnitEvent.SKIPPED_COUNT: 1,\n\t}\n\tvar report := GdUnitReport.new() \\\n\t\t.create(GdUnitReport.SKIPPED, test_case.line_number(), GdAssertMessages.test_skipped(test_case.skip_info()))\n\tfire_event(GdUnitEvent.new() \\\n\t\t.test_after(context.get_test_suite_path(),\n\t\t\tcontext.get_test_suite_name(),\n\t\t\tcontext.get_test_case_name(),\n\t\t\tstatistics,\n\t\t\t[report]))\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd.uid",
    "content": "uid://bspwipxk071tt\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd",
    "content": "## The test case startup hook implementation.[br]\n## It executes the 'test_before()' block from the test-suite.\nclass_name GdUnitTestCaseBeforeStage\nextends IGdUnitExecutionStage\n\nvar _call_stage :bool\n\n\nfunc _init(call_stage := true) -> void:\n\t_call_stage = call_stage\n\n\nfunc _execute(context :GdUnitExecutionContext) -> void:\n\tvar test_suite := context.test_suite\n\n\tfire_event(GdUnitEvent.new()\\\n\t\t.test_before(context.get_test_suite_path(), context.get_test_suite_name(), context.get_test_case_name()))\n\tif _call_stage:\n\t\t@warning_ignore(\"redundant_await\")\n\t\tawait test_suite.before_test()\n\tcontext.error_monitor_start()\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd.uid",
    "content": "uid://cfofhvwrdbgub\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseExecutionStage.gd",
    "content": "## The test case execution stage.[br]\nclass_name GdUnitTestCaseExecutionStage\nextends IGdUnitExecutionStage\n\n\nvar _stage_single_test :IGdUnitExecutionStage = GdUnitTestCaseSingleExecutionStage.new()\nvar _stage_fuzzer_test :IGdUnitExecutionStage = GdUnitTestCaseFuzzedExecutionStage.new()\nvar _stage_parameterized_test :IGdUnitExecutionStage= GdUnitTestCaseParameterizedExecutionStage.new()\n\n\n## Executes the test case 'test_<name>()'.[br]\n## It executes synchronized following stages[br]\n##  -> test_before() [br]\n##  -> test_case() [br]\n##  -> test_after() [br]\n@warning_ignore(\"redundant_await\")\nfunc _execute(context :GdUnitExecutionContext) -> void:\n\tvar test_case := context.test_case\n\n\tcontext.error_monitor_start()\n\n\tif test_case.is_parameterized():\n\t\tawait _stage_parameterized_test.execute(context)\n\telif test_case.is_fuzzed():\n\t\tawait _stage_fuzzer_test.execute(context)\n\telse:\n\t\tawait _stage_single_test.execute(context)\n\n\tawait context.gc()\n\tawait context.error_monitor_stop()\n\n\t# finally fire test statistics report\n\tfire_event(GdUnitEvent.new()\\\n\t\t.test_statistics(context.get_test_suite_path(),\n\t\t\tcontext.get_test_suite_name(),\n\t\t\tcontext.get_test_case_name(),\n\t\t\tcontext.get_execution_statistics()))\n\n\t# finally free the test instance\n\tif is_instance_valid(context.test_case):\n\t\tcontext.test_case.dispose()\n\n\nfunc set_debug_mode(debug_mode :bool = false) -> void:\n\tsuper.set_debug_mode(debug_mode)\n\t_stage_single_test.set_debug_mode(debug_mode)\n\t_stage_fuzzer_test.set_debug_mode(debug_mode)\n\t_stage_parameterized_test.set_debug_mode(debug_mode)\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseExecutionStage.gd.uid",
    "content": "uid://ch34wknnjs7bq\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd",
    "content": "## The test suite shutdown hook implementation.[br]\n## It executes the 'after()' block from the test-suite.\nclass_name GdUnitTestSuiteAfterStage\nextends IGdUnitExecutionStage\n\n\nconst GdUnitTools := preload(\"res://addons/gdUnit4/src/core/GdUnitTools.gd\")\n\n\nfunc _execute(context :GdUnitExecutionContext) -> void:\n\tvar test_suite := context.test_suite\n\n\t@warning_ignore(\"redundant_await\")\n\tawait test_suite.after()\n\tawait context.gc()\n\tvar reports := context.build_reports(false)\n\tfire_event(GdUnitEvent.new()\\\n\t\t.suite_after(context.get_test_suite_path(),\\\n\t\t\ttest_suite.get_name(),\n\t\t\tcontext.get_execution_statistics(),\n\t\t\treports))\n\n\tGdUnitFileAccess.clear_tmp()\n\t# Guard that checks if all doubled (spy/mock) objects are released\n\tGdUnitClassDoubler.check_leaked_instances()\n\t# we hide the scene/main window after runner is finished\n\tDisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_MINIMIZED)\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd.uid",
    "content": "uid://bywrdyh8iqk5m\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteBeforeStage.gd",
    "content": "## The test suite startup hook implementation.[br]\n## It executes the 'before()' block from the test-suite.\nclass_name GdUnitTestSuiteBeforeStage\nextends IGdUnitExecutionStage\n\n\nfunc _execute(context :GdUnitExecutionContext) -> void:\n\tvar test_suite := context.test_suite\n\n\tfire_event(GdUnitEvent.new()\\\n\t\t.suite_before(context.get_test_suite_path(), test_suite.get_name(), test_suite.get_child_count()))\n\n\t@warning_ignore(\"redundant_await\")\n\tawait test_suite.before()\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteBeforeStage.gd.uid",
    "content": "uid://c3tg7r3yjy73l\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd",
    "content": "## The test suite main execution stage.[br]\nclass_name GdUnitTestSuiteExecutionStage\nextends IGdUnitExecutionStage\n\nconst GdUnitTools := preload(\"res://addons/gdUnit4/src/core/GdUnitTools.gd\")\n\nvar _stage_before :IGdUnitExecutionStage = GdUnitTestSuiteBeforeStage.new()\nvar _stage_after :IGdUnitExecutionStage = GdUnitTestSuiteAfterStage.new()\nvar _stage_test :IGdUnitExecutionStage = GdUnitTestCaseExecutionStage.new()\nvar _fail_fast := false\n\n\n## Executes all tests of an test suite.[br]\n## It executes synchronized following stages[br]\n##  -> before() [br]\n##  -> run all test cases [br]\n##  -> after() [br]\nfunc _execute(context :GdUnitExecutionContext) -> void:\n\tif context.test_suite.__is_skipped:\n\t\tawait fire_test_suite_skipped(context)\n\telse:\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tGdUnitMemoryObserver.guard_instance(context.test_suite.__awaiter)\n\t\tawait _stage_before.execute(context)\n\t\tfor test_case_index in context.test_suite.get_child_count():\n\t\t\t# iterate only over test cases\n\t\t\tvar test_case := context.test_suite.get_child(test_case_index) as _TestCase\n\t\t\tif not is_instance_valid(test_case):\n\t\t\t\tcontinue\n\t\t\tcontext.test_suite.set_active_test_case(test_case.get_name())\n\t\t\tawait _stage_test.execute(GdUnitExecutionContext.of_test_case(context, test_case))\n\t\t\t# stop on first error or if fail fast is enabled\n\t\t\tif _fail_fast and not context.is_success():\n\t\t\t\tbreak\n\t\t\tif test_case.is_interupted():\n\t\t\t\t# it needs to go this hard way to kill the outstanding awaits of a test case when the test timed out\n\t\t\t\t# we delete the current test suite where is execute the current test case to kill the function state\n\t\t\t\t# and replace it by a clone without function state\n\t\t\t\tcontext.test_suite = await clone_test_suite(context.test_suite)\n\t\tawait _stage_after.execute(context)\n\t\tGdUnitMemoryObserver.unguard_instance(context.test_suite.__awaiter)\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\tcontext.test_suite.free()\n\tcontext.dispose()\n\n\n# clones a test suite and moves the test cases to new instance\nfunc clone_test_suite(test_suite :GdUnitTestSuite) -> GdUnitTestSuite:\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\tdispose_timers(test_suite)\n\tawait GdUnitMemoryObserver.gc_guarded_instance(\"Manually free on awaiter\", test_suite.__awaiter)\n\tvar parent := test_suite.get_parent()\n\tvar _test_suite := GdUnitTestSuite.new()\n\tparent.remove_child(test_suite)\n\tcopy_properties(test_suite, _test_suite)\n\tfor child in test_suite.get_children():\n\t\ttest_suite.remove_child(child)\n\t\t_test_suite.add_child(child)\n\tparent.add_child(_test_suite)\n\t@warning_ignore(\"return_value_discarded\")\n\tGdUnitMemoryObserver.guard_instance(_test_suite.__awaiter)\n\t# finally free current test suite instance\n\ttest_suite.free()\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\treturn _test_suite\n\n\nfunc dispose_timers(test_suite :GdUnitTestSuite) -> void:\n\tGdUnitTools.release_timers()\n\tfor child in test_suite.get_children():\n\t\tif child is Timer:\n\t\t\t(child as Timer).stop()\n\t\t\ttest_suite.remove_child(child)\n\t\t\tchild.free()\n\n\nfunc copy_properties(source :Object, target :Object) -> void:\n\tif not source is _TestCase and not source is GdUnitTestSuite:\n\t\treturn\n\tfor property in source.get_property_list():\n\t\tvar property_name :String = property[\"name\"]\n\t\tif property_name == \"__awaiter\":\n\t\t\tcontinue\n\t\ttarget.set(property_name, source.get(property_name))\n\n\nfunc fire_test_suite_skipped(context :GdUnitExecutionContext) -> void:\n\tvar test_suite := context.test_suite\n\tvar skip_count := test_suite.get_child_count()\n\tfire_event(GdUnitEvent.new()\\\n\t\t.suite_before(context.get_test_suite_path(), test_suite.get_name(), skip_count))\n\n\n\tfor test_case_index in context.test_suite.get_child_count():\n\t\t\t# iterate only over test cases\n\t\t\tvar test_case := context.test_suite.get_child(test_case_index) as _TestCase\n\t\t\tif not is_instance_valid(test_case):\n\t\t\t\tcontinue\n\t\t\tvar test_case_context := GdUnitExecutionContext.of_test_case(context, test_case)\n\t\t\tfire_event(GdUnitEvent.new()\\\n\t\t\t\t.test_before(test_case_context.get_test_suite_path(), test_case_context.get_test_suite_name(), test_case_context.get_test_case_name()))\n\t\t\tfire_test_skipped(test_case_context)\n\n\n\tvar statistics := {\n\t\tGdUnitEvent.ORPHAN_NODES: 0,\n\t\tGdUnitEvent.ELAPSED_TIME: 0,\n\t\tGdUnitEvent.WARNINGS: false,\n\t\tGdUnitEvent.ERRORS: false,\n\t\tGdUnitEvent.ERROR_COUNT: 0,\n\t\tGdUnitEvent.FAILED: false,\n\t\tGdUnitEvent.FAILED_COUNT: 0,\n\t\tGdUnitEvent.SKIPPED_COUNT: skip_count,\n\t\tGdUnitEvent.SKIPPED: true\n\t}\n\tvar report := GdUnitReport.new().create(GdUnitReport.SKIPPED, -1, GdAssertMessages.test_suite_skipped(test_suite.__skip_reason, skip_count))\n\tfire_event(GdUnitEvent.new().suite_after(context.get_test_suite_path(), test_suite.get_name(), statistics, [report]))\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\n\nfunc fire_test_skipped(context: GdUnitExecutionContext) -> void:\n\tvar test_case := context.test_case\n\tvar statistics := {\n\t\tGdUnitEvent.ORPHAN_NODES: 0,\n\t\tGdUnitEvent.ELAPSED_TIME: 0,\n\t\tGdUnitEvent.WARNINGS: false,\n\t\tGdUnitEvent.ERRORS: false,\n\t\tGdUnitEvent.ERROR_COUNT: 0,\n\t\tGdUnitEvent.FAILED: false,\n\t\tGdUnitEvent.FAILED_COUNT: 0,\n\t\tGdUnitEvent.SKIPPED: true,\n\t\tGdUnitEvent.SKIPPED_COUNT: 1,\n\t}\n\tvar report := GdUnitReport.new() \\\n\t\t.create(GdUnitReport.SKIPPED, test_case.line_number(), GdAssertMessages.test_skipped(\"Skipped from the entire test suite\"))\n\tfire_event(GdUnitEvent.new() \\\n\t\t.test_after(context.get_test_suite_path(),\n\t\t\tcontext.get_test_suite_name(),\n\t\t\tcontext.get_test_case_name(),\n\t\t\tstatistics,\n\t\t\t[report]))\n\t# finally fire test statistics report\n\tfire_event(GdUnitEvent.new()\\\n\t\t.test_statistics(context.get_test_suite_path(),\n\t\t\tcontext.get_test_suite_name(),\n\t\t\tcontext.get_test_case_name(),\n\t\t\tstatistics))\n\n\nfunc set_debug_mode(debug_mode :bool = false) -> void:\n\tsuper.set_debug_mode(debug_mode)\n\t_stage_before.set_debug_mode(debug_mode)\n\t_stage_after.set_debug_mode(debug_mode)\n\t_stage_test.set_debug_mode(debug_mode)\n\n\nfunc fail_fast(enabled :bool) -> void:\n\t_fail_fast = enabled\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd.uid",
    "content": "uid://bcfhaiylel827\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/IGdUnitExecutionStage.gd",
    "content": "## The interface of execution stage.[br]\n## An execution stage is defined as an encapsulated task that can execute 1-n substages covered by its own execution context.[br]\n## Execution stage are always called synchronously.\nclass_name IGdUnitExecutionStage\nextends RefCounted\n\nvar _debug_mode := false\n\n\n## Executes synchronized the implemented stage in its own execution context.[br]\n## example:[br]\n## [codeblock]\n##    # waits for 100ms\n##    await MyExecutionStage.new().execute(<GdUnitExecutionContext>)\n## [/codeblock][br]\nfunc execute(context :GdUnitExecutionContext) -> void:\n\tGdUnitThreadManager.get_current_context().set_execution_context(context)\n\t@warning_ignore(\"redundant_await\")\n\tawait _execute(context)\n\n\n## Sends the event to registered listeners\nfunc fire_event(event :GdUnitEvent) -> void:\n\tif _debug_mode:\n\t\tGdUnitSignals.instance().gdunit_event_debug.emit(event)\n\telse:\n\t\tGdUnitSignals.instance().gdunit_event.emit(event)\n\n\n## Internal testing stuff.[br]\n## Sets the executor into debug mode to emit `GdUnitEvent` via signal `gdunit_event_debug`\nfunc set_debug_mode(debug_mode :bool) -> void:\n\t_debug_mode = debug_mode\n\n\n## The execution phase to be carried out.\nfunc _execute(_context :GdUnitExecutionContext) -> void:\n\t@warning_ignore(\"assert_always_false\")\n\tassert(false, \"The execution stage is not implemented\")\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/IGdUnitExecutionStage.gd.uid",
    "content": "uid://cjh4m6x3fcuan\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedExecutionStage.gd",
    "content": "## The test case execution stage.[br]\nclass_name GdUnitTestCaseFuzzedExecutionStage\nextends IGdUnitExecutionStage\n\nvar _stage_before :IGdUnitExecutionStage = GdUnitTestCaseBeforeStage.new(false)\nvar _stage_after :IGdUnitExecutionStage = GdUnitTestCaseAfterStage.new(false)\nvar _stage_test :IGdUnitExecutionStage = GdUnitTestCaseFuzzedTestStage.new()\n\n\nfunc _execute(context :GdUnitExecutionContext) -> void:\n\twhile context.retry_execution():\n\t\tvar test_context := GdUnitExecutionContext.of(context)\n\t\tawait _stage_before.execute(test_context)\n\t\tif not context.test_case.is_skipped():\n\t\t\tawait _stage_test.execute(GdUnitExecutionContext.of(test_context))\n\t\tawait _stage_after.execute(test_context)\n\t\tif test_context.is_success() or test_context.is_skipped() or test_context.is_interupted():\n\t\t\tbreak\n\t@warning_ignore(\"return_value_discarded\")\n\tcontext.evaluate_test_retry_status()\n\n\nfunc set_debug_mode(debug_mode :bool = false) -> void:\n\tsuper.set_debug_mode(debug_mode)\n\t_stage_before.set_debug_mode(debug_mode)\n\t_stage_after.set_debug_mode(debug_mode)\n\t_stage_test.set_debug_mode(debug_mode)\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedExecutionStage.gd.uid",
    "content": "uid://hkgnxc1c5k2w\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedTestStage.gd",
    "content": "## The fuzzed test case execution stage.[br]\nclass_name GdUnitTestCaseFuzzedTestStage\nextends IGdUnitExecutionStage\n\nvar _expression_runner := GdUnitExpressionRunner.new()\n\n\n## Executes a test case with given fuzzers 'test_<name>(<fuzzer>)' iterative.[br]\n## It executes synchronized following stages[br]\n##  -> test_case() [br]\nfunc _execute(context :GdUnitExecutionContext) -> void:\n\tvar test_suite := context.test_suite\n\tvar test_case := context.test_case\n\tvar fuzzers := create_fuzzers(test_suite, test_case)\n\n\t# guard on fuzzers\n\tfor fuzzer in fuzzers:\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tGdUnitMemoryObserver.guard_instance(fuzzer)\n\n\tfor iteration in test_case.iterations():\n\t\t@warning_ignore(\"redundant_await\")\n\t\tawait test_suite.before_test()\n\t\tawait test_case.execute(fuzzers, iteration)\n\t\t@warning_ignore(\"redundant_await\")\n\t\tawait test_suite.after_test()\n\t\tif test_case.is_interupted():\n\t\t\tbreak\n\t\t# interrupt at first failure\n\t\tvar reports := context.reports()\n\t\tif not reports.is_empty():\n\t\t\tvar report :GdUnitReport = reports.pop_front()\n\t\t\treports.append(GdUnitReport.new() \\\n\t\t\t\t.create(GdUnitReport.FAILURE, report.line_number(), GdAssertMessages.fuzzer_interuped(iteration, report.message())))\n\t\t\tbreak\n\tawait context.gc()\n\n\t# unguard on fuzzers\n\tif not test_case.is_interupted():\n\t\tfor fuzzer in fuzzers:\n\t\t\tGdUnitMemoryObserver.unguard_instance(fuzzer)\n\n\nfunc create_fuzzers(test_suite :GdUnitTestSuite, test_case :_TestCase) -> Array[Fuzzer]:\n\tif not test_case.is_fuzzed():\n\t\treturn Array()\n\ttest_case.generate_seed()\n\tvar fuzzers :Array[Fuzzer] = []\n\tfor fuzzer_arg in test_case.fuzzer_arguments():\n\t\t@warning_ignore(\"unsafe_cast\")\n\t\tvar fuzzer := _expression_runner.to_fuzzer(test_suite.get_script() as GDScript, fuzzer_arg.plain_value() as String)\n\t\tfuzzer._iteration_index = 0\n\t\tfuzzer._iteration_limit = test_case.iterations()\n\t\tfuzzers.append(fuzzer)\n\treturn fuzzers\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedTestStage.gd.uid",
    "content": "uid://csqjcew3t1u2u\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/parameterized/GdUnitTestCaseParameterSetTestStage.gd",
    "content": "class_name GdUnitTestCaseParameterSetTestStage\nextends IGdUnitExecutionStage\n\n\n## Executes a parameterized test case 'test_<name>()' by given parameters.[br]\n## It executes synchronized following stages[br]\n##  -> test_case() [br]\nfunc _execute(context: GdUnitExecutionContext) -> void:\n\tawait context.test_case.execute_paramaterized(context._test_case_parameter_set)\n\tawait context.gc()\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/parameterized/GdUnitTestCaseParameterSetTestStage.gd.uid",
    "content": "uid://683m1hs31c32\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/parameterized/GdUnitTestCaseParameterizedExecutionStage.gd",
    "content": "## The test case execution stage.[br]\nclass_name GdUnitTestCaseParameterizedExecutionStage\nextends IGdUnitExecutionStage\n\n\nvar _stage_before :IGdUnitExecutionStage = GdUnitTestCaseBeforeStage.new(false)\nvar _stage_after :IGdUnitExecutionStage = GdUnitTestCaseAfterStage.new(false)\nvar _stage_test :IGdUnitExecutionStage = GdUnitTestCaseParamaterizedTestStage.new()\n\n\nfunc _execute(context :GdUnitExecutionContext) -> void:\n\tawait _stage_before.execute(context)\n\tif not context.test_case.is_skipped():\n\t\tawait _stage_test.execute(GdUnitExecutionContext.of(context))\n\tawait _stage_after.execute(context)\n\n\nfunc set_debug_mode(debug_mode :bool = false) -> void:\n\tsuper.set_debug_mode(debug_mode)\n\t_stage_before.set_debug_mode(debug_mode)\n\t_stage_after.set_debug_mode(debug_mode)\n\t_stage_test.set_debug_mode(debug_mode)\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/parameterized/GdUnitTestCaseParameterizedExecutionStage.gd.uid",
    "content": "uid://yuun5105xt6x\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/parameterized/GdUnitTestCaseParameterizedTestStage.gd",
    "content": "## The parameterized test case execution stage.[br]\nclass_name GdUnitTestCaseParamaterizedTestStage\nextends IGdUnitExecutionStage\n\nvar _stage_before: IGdUnitExecutionStage = GdUnitTestCaseBeforeStage.new()\nvar _stage_after: IGdUnitExecutionStage = GdUnitTestCaseAfterStage.new()\nvar _stage_test: IGdUnitExecutionStage = GdUnitTestCaseParameterSetTestStage.new()\n\n\n## Executes a parameterized test case.[br]\n## It executes synchronized following stages[br]\n##  -> test_case( <test_parameters> ) [br]\nfunc _execute(context: GdUnitExecutionContext) -> void:\n\tvar test_case := context.test_case\n\tvar test_parameter_index := test_case.test_parameter_index()\n\tvar parameter_set_resolver := test_case.parameter_set_resolver()\n\tvar test_names := parameter_set_resolver.build_test_case_names(test_case)\n\n\t# if all parameter sets has static values we can preload and reuse it for better performance\n\tvar parameter_sets :Array = []\n\tif parameter_set_resolver.is_parameter_sets_static():\n\t\tparameter_sets = parameter_set_resolver.load_parameter_sets(test_case, true)\n\n\tfor parameter_set_index in test_names.size():\n\t\t# is test_parameter_index is set, we run this parameterized test only\n\t\tif test_parameter_index != -1 and test_parameter_index != parameter_set_index:\n\t\t\tcontinue\n\t\tvar current_test_case_name := test_names[parameter_set_index]\n\t\tvar test_case_parameter_set: Array\n\t\tif parameter_set_resolver.is_parameter_set_static(parameter_set_index):\n\t\t\ttest_case_parameter_set = parameter_sets[parameter_set_index]\n\n\t\tvar test_context := GdUnitExecutionContext.of(context)\n\t\ttest_context._test_case_name = current_test_case_name\n\t\tvar has_errors := false\n\t\twhile test_context.retry_execution():\n\t\t\tvar retry_test_context := GdUnitExecutionContext.of(test_context)\n\n\t\t\tretry_test_context._test_case_name = current_test_case_name\n\t\t\tawait _stage_before.execute(retry_test_context)\n\t\t\tif not test_case.is_interupted():\n\t\t\t\t# we need to load paramater set at execution level after the before stage to get the actual variables from the current test\n\t\t\t\tif not parameter_set_resolver.is_parameter_set_static(parameter_set_index):\n\t\t\t\t\ttest_case_parameter_set = _load_parameter_set(context, parameter_set_index)\n\t\t\t\tawait _stage_test.execute(GdUnitExecutionContext.of_parameterized_test(retry_test_context, current_test_case_name, test_case_parameter_set))\n\t\t\tawait _stage_after.execute(retry_test_context)\n\t\t\thas_errors = retry_test_context.has_errors()\n\t\t\tif retry_test_context.is_success() or retry_test_context.is_skipped() or retry_test_context.is_interupted():\n\t\t\t\tbreak\n\n\t\tvar is_success := test_context.evaluate_test_retry_status()\n\t\treport_test_failure(context, !is_success, has_errors, parameter_set_index)\n\n\t\tif test_case.is_interupted():\n\t\t\tbreak\n\tawait context.gc()\n\n\nfunc report_test_failure(test_context: GdUnitExecutionContext, is_failed: bool, has_errors: bool, parameter_set_index: int) -> void:\n\tvar test_case := test_context.test_case\n\n\tif is_failed:\n\t\ttest_context.add_report(GdUnitReport.new().create(GdUnitReport.FAILURE, test_case.line_number(), \"Test failed at parameterized index %d.\" % parameter_set_index))\n\tif has_errors:\n\t\ttest_context.add_report(GdUnitReport.new().create(GdUnitReport.ABORT, test_case.line_number(), \"Test aborted at parameterized index %d.\" % parameter_set_index))\n\n\nfunc _load_parameter_set(context: GdUnitExecutionContext, parameter_set_index: int) -> Array:\n\tvar test_case := context.test_case\n\t# we need to exchange temporary for parameter resolving the execution context\n\t# this is necessary because of possible usage of `auto_free` and needs to run in the parent execution context\n\tvar thread_context := GdUnitThreadManager.get_current_context()\n\tvar save_execution_context := thread_context.get_execution_context()\n\tthread_context.set_execution_context(context)\n\tvar parameters := test_case.load_parameter_sets()\n\t# restore the original execution context and restart the orphan monitor to get new instances into account\n\tthread_context.set_execution_context(save_execution_context)\n\tsave_execution_context.orphan_monitor_start()\n\treturn parameters[parameter_set_index]\n\n\nfunc set_debug_mode(debug_mode: bool=false) -> void:\n\tsuper.set_debug_mode(debug_mode)\n\t_stage_before.set_debug_mode(debug_mode)\n\t_stage_after.set_debug_mode(debug_mode)\n\t_stage_test.set_debug_mode(debug_mode)\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/parameterized/GdUnitTestCaseParameterizedTestStage.gd.uid",
    "content": "uid://cn23ntdgeyw8\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleExecutionStage.gd",
    "content": "## The test case execution stage.[br]\nclass_name GdUnitTestCaseSingleExecutionStage\nextends IGdUnitExecutionStage\n\n\nvar _stage_before :IGdUnitExecutionStage = GdUnitTestCaseBeforeStage.new()\nvar _stage_after :IGdUnitExecutionStage = GdUnitTestCaseAfterStage.new()\nvar _stage_test :IGdUnitExecutionStage = GdUnitTestCaseSingleTestStage.new()\n\n\nfunc _execute(context :GdUnitExecutionContext) -> void:\n\twhile context.retry_execution():\n\t\tvar test_context := GdUnitExecutionContext.of(context)\n\t\tawait _stage_before.execute(test_context)\n\t\tif not test_context.is_skipped():\n\t\t\tawait _stage_test.execute(GdUnitExecutionContext.of(test_context))\n\t\tawait _stage_after.execute(test_context)\n\t\tif test_context.is_success() or test_context.is_skipped() or test_context.is_interupted():\n\t\t\tbreak\n\t@warning_ignore(\"return_value_discarded\")\n\tcontext.evaluate_test_retry_status()\n\n\nfunc set_debug_mode(debug_mode :bool = false) -> void:\n\tsuper.set_debug_mode(debug_mode)\n\t_stage_before.set_debug_mode(debug_mode)\n\t_stage_after.set_debug_mode(debug_mode)\n\t_stage_test.set_debug_mode(debug_mode)\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleExecutionStage.gd.uid",
    "content": "uid://dlio63n13rxux\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleTestStage.gd",
    "content": "## The single test case execution stage.[br]\nclass_name GdUnitTestCaseSingleTestStage\nextends IGdUnitExecutionStage\n\n\n## Executes a single test case 'test_<name>()'.[br]\n## It executes synchronized following stages[br]\n##  -> test_case() [br]\nfunc _execute(context :GdUnitExecutionContext) -> void:\n\tawait context.test_case.execute()\n\tawait context.gc()\n"
  },
  {
    "path": "addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleTestStage.gd.uid",
    "content": "uid://bhpclumtkvf8d\n"
  },
  {
    "path": "addons/gdUnit4/src/core/parse/GdClassDescriptor.gd",
    "content": "class_name GdClassDescriptor\nextends RefCounted\n\n\nvar _name :String\nvar _is_inner_class :bool\nvar _functions :Array[GdFunctionDescriptor]\n\n\nfunc _init(p_name :String, p_is_inner_class :bool, p_functions :Array[GdFunctionDescriptor]) -> void:\n\t_name = p_name\n\t_is_inner_class = p_is_inner_class\n\t_functions = p_functions\n\n\nfunc name() -> String:\n\treturn _name\n\n\nfunc is_inner_class() -> bool:\n\treturn _is_inner_class\n\n\nfunc functions() -> Array[GdFunctionDescriptor]:\n\treturn _functions\n"
  },
  {
    "path": "addons/gdUnit4/src/core/parse/GdClassDescriptor.gd.uid",
    "content": "uid://dolmb6t6jrcci\n"
  },
  {
    "path": "addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd",
    "content": "# holds all decodings for default values\nclass_name GdDefaultValueDecoder\nextends GdUnitSingleton\n\n\n@warning_ignore(\"unused_parameter\")\nvar _decoders := {\n\tTYPE_NIL: func(value :Variant) -> String: return \"null\",\n\tTYPE_STRING: func(value :Variant) -> String: return '\"%s\"' % value,\n\tTYPE_STRING_NAME: _on_type_StringName,\n\tTYPE_BOOL: func(value :Variant) -> String: return str(value).to_lower(),\n\tTYPE_FLOAT: func(value :Variant) -> String: return '%f' % value,\n\tTYPE_COLOR: _on_type_Color,\n\tTYPE_ARRAY: _on_type_Array.bind(TYPE_ARRAY),\n\tTYPE_PACKED_BYTE_ARRAY: _on_type_Array.bind(TYPE_PACKED_BYTE_ARRAY),\n\tTYPE_PACKED_STRING_ARRAY: _on_type_Array.bind(TYPE_PACKED_STRING_ARRAY),\n\tTYPE_PACKED_FLOAT32_ARRAY: _on_type_Array.bind(TYPE_PACKED_FLOAT32_ARRAY),\n\tTYPE_PACKED_FLOAT64_ARRAY: _on_type_Array.bind(TYPE_PACKED_FLOAT64_ARRAY),\n\tTYPE_PACKED_INT32_ARRAY: _on_type_Array.bind(TYPE_PACKED_INT32_ARRAY),\n\tTYPE_PACKED_INT64_ARRAY: _on_type_Array.bind(TYPE_PACKED_INT64_ARRAY),\n\tTYPE_PACKED_COLOR_ARRAY: _on_type_Array.bind(TYPE_PACKED_COLOR_ARRAY),\n\tTYPE_PACKED_VECTOR2_ARRAY: _on_type_Array.bind(TYPE_PACKED_VECTOR2_ARRAY),\n\tTYPE_PACKED_VECTOR3_ARRAY: _on_type_Array.bind(TYPE_PACKED_VECTOR3_ARRAY),\n\tGdObjects.TYPE_PACKED_VECTOR4_ARRAY: _on_type_Array.bind(GdObjects.TYPE_PACKED_VECTOR4_ARRAY),\n\tTYPE_DICTIONARY: _on_type_Dictionary,\n\tTYPE_RID: _on_type_RID,\n\tTYPE_NODE_PATH: _on_type_NodePath,\n\tTYPE_VECTOR2: _on_type_Vector.bind(TYPE_VECTOR2),\n\tTYPE_VECTOR2I: _on_type_Vector.bind(TYPE_VECTOR2I),\n\tTYPE_VECTOR3: _on_type_Vector.bind(TYPE_VECTOR3),\n\tTYPE_VECTOR3I: _on_type_Vector.bind(TYPE_VECTOR3I),\n\tTYPE_VECTOR4: _on_type_Vector.bind(TYPE_VECTOR4),\n\tTYPE_VECTOR4I: _on_type_Vector.bind(TYPE_VECTOR4I),\n\tTYPE_RECT2: _on_type_Rect2,\n\tTYPE_RECT2I: _on_type_Rect2i,\n\tTYPE_PLANE: _on_type_Plane,\n\tTYPE_QUATERNION: _on_type_Quaternion,\n\tTYPE_AABB: _on_type_AABB,\n\tTYPE_BASIS: _on_type_Basis,\n\tTYPE_CALLABLE: _on_type_Callable,\n\tTYPE_SIGNAL: _on_type_Signal,\n\tTYPE_TRANSFORM2D: _on_type_Transform2D,\n\tTYPE_TRANSFORM3D: _on_type_Transform3D,\n\tTYPE_PROJECTION: _on_type_Projection,\n\tTYPE_OBJECT: _on_type_Object\n}\n\nstatic func _regex(pattern :String) -> RegEx:\n\tvar regex := RegEx.new()\n\tvar err := regex.compile(pattern)\n\tif err != OK:\n\t\tpush_error(\"error '%s' checked pattern '%s'\" % [err, pattern])\n\t\treturn null\n\treturn regex\n\n\nfunc get_decoder(type :int) -> Callable:\n\treturn _decoders.get(type, func(value :Variant) -> String: return '%s' % value)\n\n\nfunc _on_type_StringName(value :StringName) -> String:\n\tif value.is_empty():\n\t\treturn 'StringName()'\n\treturn 'StringName(\"%s\")' % value\n\n\nfunc _on_type_Object(value: Variant, _type: int) -> String:\n\treturn str(value)\n\n\nfunc _on_type_Color(color: Color) -> String:\n\tif color == Color.BLACK:\n\t\treturn \"Color()\"\n\treturn \"Color%s\" % color\n\n\nfunc _on_type_NodePath(path :NodePath) -> String:\n\tif path.is_empty():\n\t\treturn 'NodePath()'\n\treturn 'NodePath(\"%s\")' % path\n\n\nfunc _on_type_Callable(_cb :Callable) -> String:\n\treturn 'Callable()'\n\n\nfunc _on_type_Signal(_s :Signal) -> String:\n\treturn 'Signal()'\n\n\nfunc _on_type_Dictionary(dict :Dictionary) -> String:\n\tif dict.is_empty():\n\t\treturn '{}'\n\treturn str(dict)\n\n\nfunc _on_type_Array(value :Variant, type :int) -> String:\n\tmatch type:\n\t\tTYPE_ARRAY:\n\t\t\treturn str(value)\n\n\t\tTYPE_PACKED_COLOR_ARRAY:\n\t\t\tvar colors := PackedStringArray()\n\t\t\tfor color: Color in value:\n\t\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t\tcolors.append(_on_type_Color(color))\n\t\t\tif colors.is_empty():\n\t\t\t\treturn \"PackedColorArray()\"\n\t\t\treturn \"PackedColorArray([%s])\" % \", \".join(colors)\n\n\t\tTYPE_PACKED_VECTOR2_ARRAY:\n\t\t\tvar vectors := PackedStringArray()\n\t\t\tfor vector: Vector2 in value:\n\t\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t\tvectors.append(_on_type_Vector(vector, TYPE_VECTOR2))\n\t\t\tif vectors.is_empty():\n\t\t\t\treturn \"PackedVector2Array()\"\n\t\t\treturn \"PackedVector2Array([%s])\" % \", \".join(vectors)\n\n\t\tTYPE_PACKED_VECTOR3_ARRAY:\n\t\t\tvar vectors := PackedStringArray()\n\t\t\tfor vector: Vector3 in value:\n\t\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t\tvectors.append(_on_type_Vector(vector, TYPE_VECTOR3))\n\t\t\tif vectors.is_empty():\n\t\t\t\treturn \"PackedVector3Array()\"\n\t\t\treturn \"PackedVector3Array([%s])\" % \", \".join(vectors)\n\n\t\tGdObjects.TYPE_PACKED_VECTOR4_ARRAY:\n\t\t\tvar vectors := PackedStringArray()\n\t\t\tfor vector: Vector4 in value:\n\t\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t\tvectors.append(_on_type_Vector(vector, TYPE_VECTOR4))\n\t\t\tif vectors.is_empty():\n\t\t\t\treturn \"PackedVector4Array()\"\n\t\t\treturn \"PackedVector4Array([%s])\" % \", \".join(vectors)\n\n\t\tTYPE_PACKED_STRING_ARRAY:\n\t\t\tvar values := PackedStringArray()\n\t\t\tfor v: String in value:\n\t\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t\tvalues.append('\"%s\"' % v)\n\t\t\tif values.is_empty():\n\t\t\t\treturn \"PackedStringArray()\"\n\t\t\treturn \"PackedStringArray([%s])\" % \", \".join(values)\n\n\t\tTYPE_PACKED_BYTE_ARRAY,\\\n\t\tTYPE_PACKED_FLOAT32_ARRAY,\\\n\t\tTYPE_PACKED_FLOAT64_ARRAY,\\\n\t\tTYPE_PACKED_INT32_ARRAY,\\\n\t\tTYPE_PACKED_INT64_ARRAY:\n\t\t\tvar vectors := PackedStringArray()\n\t\t\tfor vector :Variant in value:\n\t\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t\tvectors.append(str(vector))\n\t\t\tif vectors.is_empty():\n\t\t\t\treturn GdObjects.type_as_string(type) + \"()\"\n\t\t\treturn \"%s([%s])\" % [GdObjects.type_as_string(type), \", \".join(vectors)]\n\treturn \"unknown array type %d\" % type\n\n\nfunc _on_type_Vector(value :Variant, type :int) -> String:\n\tmatch type:\n\t\tTYPE_VECTOR2:\n\t\t\tif value == Vector2():\n\t\t\t\treturn \"Vector2()\"\n\t\t\treturn \"Vector2%s\" % value\n\t\tTYPE_VECTOR2I:\n\t\t\tif value == Vector2i():\n\t\t\t\treturn \"Vector2i()\"\n\t\t\treturn \"Vector2i%s\" % value\n\t\tTYPE_VECTOR3:\n\t\t\tif value == Vector3():\n\t\t\t\treturn \"Vector3()\"\n\t\t\treturn \"Vector3%s\" % value\n\t\tTYPE_VECTOR3I:\n\t\t\tif value == Vector3i():\n\t\t\t\treturn \"Vector3i()\"\n\t\t\treturn \"Vector3i%s\" % value\n\t\tTYPE_VECTOR4:\n\t\t\tif value == Vector4():\n\t\t\t\treturn \"Vector4()\"\n\t\t\treturn \"Vector4%s\" % value\n\t\tTYPE_VECTOR4I:\n\t\t\tif value == Vector4i():\n\t\t\t\treturn \"Vector4i()\"\n\t\t\treturn \"Vector4i%s\" % value\n\treturn \"unknown vector type %d\" % type\n\n\nfunc _on_type_Transform2D(transform :Transform2D) -> String:\n\tif transform == Transform2D():\n\t\treturn \"Transform2D()\"\n\treturn \"Transform2D(Vector2%s, Vector2%s, Vector2%s)\" % [transform.x, transform.y, transform.origin]\n\n\nfunc _on_type_Transform3D(transform :Transform3D) -> String:\n\tif transform == Transform3D():\n\t\treturn \"Transform3D()\"\n\treturn \"Transform3D(Vector3%s, Vector3%s, Vector3%s, Vector3%s)\" % [transform.basis.x, transform.basis.y, transform.basis.z, transform.origin]\n\n\nfunc _on_type_Projection(projection :Projection) -> String:\n\treturn \"Projection(Vector4%s, Vector4%s, Vector4%s, Vector4%s)\" % [projection.x, projection.y, projection.z, projection.w]\n\n\n@warning_ignore(\"unused_parameter\")\nfunc _on_type_RID(value :RID) -> String:\n\treturn \"RID()\"\n\n\nfunc _on_type_Rect2(rect :Rect2) -> String:\n\tif rect == Rect2():\n\t\treturn \"Rect2()\"\n\treturn \"Rect2(Vector2%s, Vector2%s)\" % [rect.position, rect.size]\n\n\nfunc _on_type_Rect2i(rect :Variant) -> String:\n\tif rect == Rect2i():\n\t\treturn \"Rect2i()\"\n\treturn \"Rect2i(Vector2i%s, Vector2i%s)\" % [rect.position, rect.size]\n\n\nfunc _on_type_Plane(plane :Plane) -> String:\n\tif plane == Plane():\n\t\treturn \"Plane()\"\n\treturn \"Plane(%d, %d, %d, %d)\" % [plane.x, plane.y, plane.z, plane.d]\n\n\nfunc _on_type_Quaternion(quaternion :Quaternion) -> String:\n\tif quaternion == Quaternion():\n\t\treturn \"Quaternion()\"\n\treturn \"Quaternion(%d, %d, %d, %d)\" % [quaternion.x, quaternion.y, quaternion.z, quaternion.w]\n\n\nfunc _on_type_AABB(aabb :AABB) -> String:\n\tif aabb == AABB():\n\t\treturn \"AABB()\"\n\treturn \"AABB(Vector3%s, Vector3%s)\" % [aabb.position, aabb.size]\n\n\nfunc _on_type_Basis(basis :Basis) -> String:\n\tif basis == Basis():\n\t\treturn \"Basis()\"\n\treturn \"Basis(Vector3%s, Vector3%s, Vector3%s)\" % [basis.x, basis.y, basis.z]\n\n\n@warning_ignore(\"unsafe_cast\")\nstatic func decode(value :Variant) -> String:\n\tvar type := typeof(value)\n\tif GdArrayTools.is_type_array(type) and (value as Array).is_empty():\n\t\treturn \"<empty>\"\n\tvar decoder :Callable = (\n\t\t\tinstance(\"GdUnitDefaultValueDecoders\",\n\t\t\t\tfunc() -> GdDefaultValueDecoder: return GdDefaultValueDecoder.new()\n\t\t\t\t) as GdDefaultValueDecoder\n\t\t).get_decoder(type)\n\tif decoder == null:\n\t\tpush_error(\"No value decoder registered for type '%d'! Please open a Bug issue at 'https://github.com/MikeSchulze/gdUnit4/issues/new/choose'.\" % type)\n\t\treturn \"null\"\n\tif type == TYPE_OBJECT:\n\t\treturn decoder.call(value, type)\n\treturn decoder.call(value)\n\n\n@warning_ignore(\"unsafe_cast\")\nstatic func decode_typed(type :int, value :Variant) -> String:\n\tif value == null:\n\t\treturn \"null\"\n\tvar decoder: Callable = (\n\t\t\tinstance(\"GdUnitDefaultValueDecoders\",\n\t\t\t\tfunc() -> GdDefaultValueDecoder: return GdDefaultValueDecoder.new()\n\t\t\t\t) as GdDefaultValueDecoder\n\t\t\t).get_decoder(type)\n\tif decoder == null:\n\t\tpush_error(\"No value decoder registered for type '%d'! Please open a Bug issue at 'https://github.com/MikeSchulze/gdUnit4/issues/new/choose'.\" % type)\n\t\treturn \"null\"\n\tif type == TYPE_OBJECT:\n\t\treturn decoder.call(value, type)\n\treturn decoder.call(value)\n"
  },
  {
    "path": "addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd.uid",
    "content": "uid://cvddqlbrpf3gp\n"
  },
  {
    "path": "addons/gdUnit4/src/core/parse/GdFunctionArgument.gd",
    "content": "class_name GdFunctionArgument\nextends RefCounted\n\n\nconst GdUnitTools := preload(\"res://addons/gdUnit4/src/core/GdUnitTools.gd\")\nconst UNDEFINED: String = \"<-NO_ARG->\"\nconst ARG_PARAMETERIZED_TEST := \"test_parameters\"\n\nstatic var _fuzzer_regex: RegEx\nstatic var _cleanup_leading_spaces: RegEx\nstatic var _fix_comma_space: RegEx\n\nvar _name: String\nvar _type: int\nvar _type_hint: int\nvar _default_value: Variant\nvar _parameter_sets: PackedStringArray = []\n\n\nfunc _init(p_name: String, p_type: int, value: Variant = UNDEFINED, p_type_hint: int = TYPE_NIL) -> void:\n\t_init_static_variables()\n\t_name = p_name\n\t_type = p_type\n\t_type_hint = p_type_hint\n\tif value != null and p_name == ARG_PARAMETERIZED_TEST:\n\t\t_parameter_sets = _parse_parameter_set(str(value))\n\t_default_value = value\n\t# is argument a fuzzer?\n\tif _type == TYPE_OBJECT and _fuzzer_regex.search(_name):\n\t\t_type = GdObjects.TYPE_FUZZER\n\n\nfunc _init_static_variables() -> void:\n\tif _fuzzer_regex == null:\n\t\t_fuzzer_regex = GdUnitTools.to_regex(\"((?!(fuzzer_(seed|iterations)))fuzzer?\\\\w+)( ?+= ?+| ?+:= ?+| ?+:Fuzzer ?+= ?+|)\")\n\t\t_cleanup_leading_spaces = RegEx.create_from_string(\"(?m)^[ \\t]+\")\n\t\t_fix_comma_space = RegEx.create_from_string(\"\"\", {0,}\\t{0,}(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)(?!\\\\s)\"\"\")\n\n\nfunc name() -> String:\n\treturn _name\n\n\nfunc default() -> Variant:\n\treturn GodotVersionFixures.convert(_default_value, _type)\n\n\nfunc set_value(value: String) -> void:\n\t# we onle need to apply default values for Objects, all others are provided by the method descriptor\n\tif _type == GdObjects.TYPE_FUZZER:\n\t\t_default_value = value\n\t\treturn\n\tif _name == ARG_PARAMETERIZED_TEST:\n\t\t_parameter_sets = _parse_parameter_set(value)\n\t\t_default_value = value\n\t\treturn\n\n\tif _type == TYPE_NIL or _type == GdObjects.TYPE_VARIANT:\n\t\t_type = _extract_value_type(value)\n\t\t_default_value = value\n\tif _default_value == null:\n\t\t_default_value = value\n\n\nfunc _extract_value_type(value: String) -> int:\n\tif value != UNDEFINED:\n\t\tif _fuzzer_regex.search(_name):\n\t\t\treturn GdObjects.TYPE_FUZZER\n\t\tif value.rfind(\")\") == value.length()-1:\n\t\t\treturn GdObjects.TYPE_FUNC\n\treturn _type\n\n\nfunc value_as_string() -> String:\n\tif has_default():\n\t\treturn GdDefaultValueDecoder.decode_typed(_type, _default_value)\n\treturn \"\"\n\n\nfunc plain_value() -> Variant:\n\treturn _default_value\n\n\nfunc type() -> int:\n\treturn _type\n\n\nfunc type_hint() -> int:\n\treturn _type_hint\n\n\nfunc has_default() -> bool:\n\treturn not is_same(_default_value, UNDEFINED)\n\n\nfunc is_typed_array() -> bool:\n\treturn _type == TYPE_ARRAY and _type_hint != TYPE_NIL\n\n\nfunc is_parameter_set() -> bool:\n\treturn _name == ARG_PARAMETERIZED_TEST\n\n\nfunc parameter_sets() -> PackedStringArray:\n\treturn _parameter_sets\n\n\nstatic func get_parameter_set(parameters :Array[GdFunctionArgument]) -> GdFunctionArgument:\n\tfor current in parameters:\n\t\tif current != null and current.is_parameter_set():\n\t\t\treturn current\n\treturn null\n\n\nfunc _to_string() -> String:\n\tvar s := _name\n\tif _type != TYPE_NIL:\n\t\ts += \":\" + GdObjects.type_as_string(_type)\n\tif _type_hint != TYPE_NIL:\n\t\ts += \"[%s]\" % GdObjects.type_as_string(_type_hint)\n\tif typeof(_default_value) != TYPE_STRING:\n\t\ts += \"=\" + value_as_string()\n\treturn s\n\n\nfunc _parse_parameter_set(input :String) -> PackedStringArray:\n\tif not input.contains(\"[\"):\n\t\treturn []\n\n\tinput = _cleanup_leading_spaces.sub(input, \"\", true)\n\tinput = input.replace(\"\\n\", \"\").strip_edges().trim_prefix(\"[\").trim_suffix(\"]\").trim_prefix(\"]\")\n\tvar single_quote := false\n\tvar double_quote := false\n\tvar array_end := 0\n\tvar current_index := 0\n\tvar output :PackedStringArray = []\n\tvar buf := input.to_utf8_buffer()\n\tvar collected_characters: = PackedByteArray()\n\tvar matched :bool = false\n\n\tfor c in buf:\n\t\tcurrent_index += 1\n\t\tmatched = current_index == buf.size()\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tcollected_characters.push_back(c)\n\n\t\tmatch c:\n\t\t\t# ' ': ignore spaces between array elements\n\t\t\t32: if array_end == 0 and (not double_quote and not single_quote):\n\t\t\t\t\tcollected_characters.remove_at(collected_characters.size()-1)\n\t\t\t# ',': step over array element seperator ','\n\t\t\t44: if array_end == 0:\n\t\t\t\t\tmatched = true\n\t\t\t\t\tcollected_characters.remove_at(collected_characters.size()-1)\n\t\t\t# '`':\n\t\t\t39: single_quote = !single_quote\n\t\t\t# '\"':\n\t\t\t34: if not single_quote: double_quote = !double_quote\n\t\t\t# '['\n\t\t\t91: if not double_quote and not single_quote: array_end +=1 # counts array open\n\t\t\t# ']'\n\t\t\t93: if not double_quote and not single_quote: array_end -=1 # counts array closed\n\n\t\t# if array closed than collect the element\n\t\tif matched:\n\t\t\tvar parameters := _fix_comma_space.sub(collected_characters.get_string_from_utf8(), \", \", true)\n\t\t\tif not parameters.is_empty():\n\t\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t\toutput.append(parameters)\n\t\t\tcollected_characters.clear()\n\t\t\tmatched = false\n\treturn output\n"
  },
  {
    "path": "addons/gdUnit4/src/core/parse/GdFunctionArgument.gd.uid",
    "content": "uid://dkwsv4clksqm5\n"
  },
  {
    "path": "addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd",
    "content": "class_name GdFunctionDescriptor\nextends RefCounted\n\nvar _is_virtual :bool\nvar _is_static :bool\nvar _is_engine :bool\nvar _is_coroutine :bool\nvar _name :String\nvar _source_path: String\nvar _line_number :int\nvar _return_type :int\nvar _return_class :String\nvar _args : Array[GdFunctionArgument]\nvar _varargs :Array[GdFunctionArgument]\n\n\n\nstatic func create(p_name: String, p_source_path: String, p_source_line: int, p_return_type: int, p_args: Array[GdFunctionArgument] = []) -> GdFunctionDescriptor:\n\tvar fd := GdFunctionDescriptor.new(p_name, p_source_line, false, false, false, p_return_type, \"\", p_args)\n\tfd.enrich_file_info(p_source_path, p_source_line)\n\treturn fd\n\nstatic func create_static(p_name: String, p_source_path: String, p_source_line: int, p_return_type: int, p_args: Array[GdFunctionArgument] = []) -> GdFunctionDescriptor:\n\tvar fd := GdFunctionDescriptor.new(p_name, p_source_line, false, true, false, p_return_type, \"\", p_args)\n\tfd.enrich_file_info(p_source_path, p_source_line)\n\treturn fd\n\n\nfunc _init(p_name :String,\n\tp_line_number :int,\n\tp_is_virtual :bool,\n\tp_is_static :bool,\n\tp_is_engine :bool,\n\tp_return_type :int,\n\tp_return_class :String,\n\tp_args : Array[GdFunctionArgument],\n\tp_varargs :Array[GdFunctionArgument] = []) -> void:\n\t_name = p_name\n\t_line_number = p_line_number\n\t_return_type = p_return_type\n\t_return_class = p_return_class\n\t_is_virtual = p_is_virtual\n\t_is_static = p_is_static\n\t_is_engine = p_is_engine\n\t_is_coroutine = false\n\t_args = p_args\n\t_varargs = p_varargs\n\n\nfunc with_return_class(clazz_name: String) -> GdFunctionDescriptor:\n\t_return_class = clazz_name\n\treturn self\n\n\nfunc name() -> String:\n\treturn _name\n\n\nfunc source_path() -> String:\n\treturn _source_path\n\n\nfunc line_number() -> int:\n\treturn _line_number\n\n\nfunc is_virtual() -> bool:\n\treturn _is_virtual\n\n\nfunc is_static() -> bool:\n\treturn _is_static\n\n\nfunc is_engine() -> bool:\n\treturn _is_engine\n\n\nfunc is_vararg() -> bool:\n\treturn not _varargs.is_empty()\n\n\nfunc is_coroutine() -> bool:\n\treturn _is_coroutine\n\n\nfunc is_parameterized() -> bool:\n\tfor current in _args:\n\t\tvar arg :GdFunctionArgument = current\n\t\tif arg.name() == GdFunctionArgument.ARG_PARAMETERIZED_TEST:\n\t\t\treturn true\n\treturn false\n\n\nfunc is_private() -> bool:\n\treturn name().begins_with(\"_\") and not is_virtual()\n\n\nfunc return_type() -> int:\n\treturn _return_type\n\n\nfunc return_type_as_string() -> String:\n\tif (return_type() == TYPE_OBJECT or return_type() == GdObjects.TYPE_ENUM) and not _return_class.is_empty():\n\t\treturn _return_class\n\treturn GdObjects.type_as_string(return_type())\n\n\n@warning_ignore(\"unsafe_cast\")\nfunc set_argument_value(arg_name: String, value: String) -> void:\n\t(\n\t\t_args.filter(func(arg: GdFunctionArgument) -> bool: return arg.name() == arg_name)\\\n\t\t.front() as GdFunctionArgument\n\t).set_value(value)\n\n\nfunc enrich_file_info(p_source_path: String, p_line_number: int) -> void:\n\t_source_path = p_source_path\n\t_line_number = p_line_number\n\n\nfunc args() -> Array[GdFunctionArgument]:\n\treturn _args\n\n\nfunc varargs() -> Array[GdFunctionArgument]:\n\treturn _varargs\n\n\nfunc typed_args() -> String:\n\tvar collect := PackedStringArray()\n\tfor arg in args():\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tcollect.push_back(arg._to_string())\n\tfor arg in varargs():\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tcollect.push_back(arg._to_string())\n\treturn \", \".join(collect)\n\n\nfunc _to_string() -> String:\n\tvar fsignature := \"virtual \" if is_virtual() else \"\"\n\tif _return_type == TYPE_NIL:\n\t\treturn fsignature + \"[Line:%s] func %s(%s):\" % [line_number(), name(), typed_args()]\n\tvar func_template := fsignature + \"[Line:%s] func %s(%s) -> %s:\"\n\tif is_static():\n\t\tfunc_template= \"[Line:%s] static func %s(%s) -> %s:\"\n\treturn func_template % [line_number(), name(), typed_args(), return_type_as_string()]\n\n\n# extract function description given by Object.get_method_list()\nstatic func extract_from(descriptor :Dictionary, is_engine_ := true) -> GdFunctionDescriptor:\n\tvar func_name: String = descriptor[\"name\"]\n\tvar function_flags: int = descriptor[\"flags\"]\n\tvar return_descriptor: Dictionary = descriptor[\"return\"]\n\tvar clazz_name: String = return_descriptor[\"class_name\"]\n\tvar is_virtual_: bool = function_flags & METHOD_FLAG_VIRTUAL\n\tvar is_static_: bool = function_flags & METHOD_FLAG_STATIC\n\tvar is_vararg_: bool = function_flags & METHOD_FLAG_VARARG\n\n\treturn GdFunctionDescriptor.new(\n\t\tfunc_name,\n\t\t-1,\n\t\tis_virtual_,\n\t\tis_static_,\n\t\tis_engine_,\n\t\t_extract_return_type(return_descriptor),\n\t\tclazz_name,\n\t\t_extract_args(descriptor),\n\t\t_build_varargs(is_vararg_)\n\t)\n\n# temporary exclude GlobalScope enums\nconst enum_fix := [\n\t\"Side\",\n\t\"Corner\",\n\t\"Orientation\",\n\t\"ClockDirection\",\n\t\"HorizontalAlignment\",\n\t\"VerticalAlignment\",\n\t\"InlineAlignment\",\n\t\"EulerOrder\",\n\t\"Error\",\n\t\"Key\",\n\t\"MIDIMessage\",\n\t\"MouseButton\",\n\t\"MouseButtonMask\",\n\t\"JoyButton\",\n\t\"JoyAxis\",\n\t\"PropertyHint\",\n\t\"PropertyUsageFlags\",\n\t\"MethodFlags\",\n\t\"Variant.Type\",\n\t\"Control.LayoutMode\"]\n\n\nstatic func _extract_return_type(return_info :Dictionary) -> int:\n\tvar type :int = return_info[\"type\"]\n\tvar usage :int = return_info[\"usage\"]\n\tif type == TYPE_INT and usage & PROPERTY_USAGE_CLASS_IS_ENUM:\n\t\treturn GdObjects.TYPE_ENUM\n\tif type == TYPE_NIL and usage & PROPERTY_USAGE_NIL_IS_VARIANT:\n\t\treturn GdObjects.TYPE_VARIANT\n\tif type == TYPE_NIL and usage == 6:\n\t\treturn GdObjects.TYPE_VOID\n\treturn type\n\n\nstatic func _extract_args(descriptor :Dictionary) -> Array[GdFunctionArgument]:\n\tvar args_ :Array[GdFunctionArgument] = []\n\tvar arguments :Array = descriptor[\"args\"]\n\tvar defaults :Array = descriptor[\"default_args\"]\n\t# iterate backwards because the default values are stored from right to left\n\twhile not arguments.is_empty():\n\t\tvar arg :Dictionary = arguments.pop_back()\n\t\tvar arg_name := _argument_name(arg)\n\t\tvar arg_type := _argument_type(arg)\n\t\tvar arg_type_hint := _argument_hint(arg)\n\t\t#var arg_class: StringName = arg[\"class_name\"]\n\t\tvar default_value: Variant = GdFunctionArgument.UNDEFINED if defaults.is_empty() else defaults.pop_back()\n\t\targs_.push_front(GdFunctionArgument.new(arg_name, arg_type, default_value, arg_type_hint))\n\treturn args_\n\n\nstatic func _build_varargs(p_is_vararg :bool) -> Array[GdFunctionArgument]:\n\tvar varargs_ :Array[GdFunctionArgument] = []\n\tif not p_is_vararg:\n\t\treturn varargs_\n\t# if function has vararg we need to handle this manually by adding 10 default arguments\n\tvar type := GdObjects.TYPE_VARARG\n\tfor index in 10:\n\t\tvarargs_.push_back(GdFunctionArgument.new(\"vararg%d_\" % index, type, '\"%s\"' % GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE))\n\treturn varargs_\n\n\nstatic func _argument_name(arg :Dictionary) -> String:\n\treturn arg[\"name\"]\n\n\nstatic func _argument_type(arg :Dictionary) -> int:\n\tvar type :int = arg[\"type\"]\n\tvar usage :int = arg[\"usage\"]\n\n\tif type == TYPE_OBJECT:\n\t\tif arg[\"class_name\"] == \"Node\":\n\t\t\treturn GdObjects.TYPE_NODE\n\t\tif arg[\"class_name\"] == \"Fuzzer\":\n\t\t\treturn GdObjects.TYPE_FUZZER\n\n\t# if the argument untyped we need to scan the assignef value type\n\tif type == TYPE_NIL and usage == PROPERTY_USAGE_NIL_IS_VARIANT:\n\t\treturn GdObjects.TYPE_VARIANT\n\treturn type\n\n\nstatic func _argument_hint(arg :Dictionary) -> int:\n\tvar hint :int = arg[\"hint\"]\n\tvar hint_string :String = arg[\"hint_string\"]\n\n\tmatch hint:\n\t\tPROPERTY_HINT_ARRAY_TYPE:\n\t\t\treturn GdObjects.string_to_type(hint_string)\n\t\t_:\n\t\t\treturn 0\n\n\nstatic func _argument_type_as_string(arg :Dictionary) -> String:\n\tvar type := _argument_type(arg)\n\tmatch type:\n\t\tTYPE_NIL:\n\t\t\treturn \"\"\n\t\tTYPE_OBJECT:\n\t\t\tvar clazz_name :String = arg[\"class_name\"]\n\t\t\tif not clazz_name.is_empty():\n\t\t\t\treturn clazz_name\n\t\t\treturn \"\"\n\t\t_:\n\t\t\treturn GdObjects.type_as_string(type)\n"
  },
  {
    "path": "addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd.uid",
    "content": "uid://bwyqedq3up816\n"
  },
  {
    "path": "addons/gdUnit4/src/core/parse/GdScriptParser.gd",
    "content": "class_name GdScriptParser\nextends RefCounted\n\nconst GdUnitTools := preload(\"res://addons/gdUnit4/src/core/GdUnitTools.gd\")\n\nconst ALLOWED_CHARACTERS := \"0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\\\"\"\n\nvar TOKEN_NOT_MATCH := Token.new(\"\")\nvar TOKEN_SPACE := SkippableToken.new(\" \")\nvar TOKEN_TABULATOR := SkippableToken.new(\"\\t\")\nvar TOKEN_NEW_LINE := SkippableToken.new(\"\\n\")\nvar TOKEN_COMMENT := SkippableToken.new(\"#\")\nvar TOKEN_CLASS_NAME := Token.new(\"class_name\")\nvar TOKEN_INNER_CLASS := Token.new(\"class\")\nvar TOKEN_EXTENDS := Token.new(\"extends\")\nvar TOKEN_ENUM := Token.new(\"enum\")\nvar TOKEN_FUNCTION_STATIC_DECLARATION := Token.new(\"static func\")\nvar TOKEN_FUNCTION_DECLARATION := Token.new(\"func\")\nvar TOKEN_FUNCTION := Token.new(\".\")\nvar TOKEN_FUNCTION_RETURN_TYPE := Token.new(\"->\")\nvar TOKEN_FUNCTION_END := Token.new(\"):\")\nvar TOKEN_ARGUMENT_ASIGNMENT := Token.new(\"=\")\nvar TOKEN_ARGUMENT_TYPE_ASIGNMENT := Token.new(\":=\")\nvar TOKEN_ARGUMENT_FUZZER := FuzzerToken.new(GdUnitTools.to_regex(\"((?!(fuzzer_(seed|iterations)))fuzzer?\\\\w+)( ?+= ?+| ?+:= ?+| ?+:Fuzzer ?+= ?+|)\"))\nvar TOKEN_ARGUMENT_TYPE := Token.new(\":\")\nvar TOKEN_ARGUMENT_SEPARATOR := Token.new(\",\")\nvar TOKEN_BRACKET_OPEN := Token.new(\"(\")\nvar TOKEN_BRACKET_CLOSE := Token.new(\")\")\nvar TOKEN_ARRAY_OPEN := Token.new(\"[\")\nvar TOKEN_ARRAY_CLOSE := Token.new(\"]\")\n\nvar OPERATOR_ADD := Operator.new(\"+\")\nvar OPERATOR_SUB := Operator.new(\"-\")\nvar OPERATOR_MUL := Operator.new(\"*\")\nvar OPERATOR_DIV := Operator.new(\"/\")\nvar OPERATOR_REMAINDER := Operator.new(\"%\")\n\nvar TOKENS :Array[Token] = [\n\tTOKEN_SPACE,\n\tTOKEN_TABULATOR,\n\tTOKEN_NEW_LINE,\n\tTOKEN_COMMENT,\n\tTOKEN_BRACKET_OPEN,\n\tTOKEN_BRACKET_CLOSE,\n\tTOKEN_ARRAY_OPEN,\n\tTOKEN_ARRAY_CLOSE,\n\tTOKEN_CLASS_NAME,\n\tTOKEN_INNER_CLASS,\n\tTOKEN_EXTENDS,\n\tTOKEN_ENUM,\n\tTOKEN_FUNCTION_STATIC_DECLARATION,\n\tTOKEN_FUNCTION_DECLARATION,\n\tTOKEN_ARGUMENT_FUZZER,\n\tTOKEN_ARGUMENT_TYPE_ASIGNMENT,\n\tTOKEN_ARGUMENT_ASIGNMENT,\n\tTOKEN_ARGUMENT_TYPE,\n\tTOKEN_FUNCTION,\n\tTOKEN_ARGUMENT_SEPARATOR,\n\tTOKEN_FUNCTION_RETURN_TYPE,\n\tOPERATOR_ADD,\n\tOPERATOR_SUB,\n\tOPERATOR_MUL,\n\tOPERATOR_DIV,\n\tOPERATOR_REMAINDER,\n]\n\nvar _regex_clazz_name := GdUnitTools.to_regex(\"(class) ([a-zA-Z0-9_]+) (extends[a-zA-Z]+:)|(class) ([a-zA-Z0-9_]+)\")\nvar _regex_strip_comments := GdUnitTools.to_regex(\"^([^#\\\"']|'[^']*'|\\\"[^\\\"]*\\\")*\\\\K#.*\")\nvar _scanned_inner_classes := PackedStringArray()\nvar _script_constants := {}\n\n\nstatic func to_unix_format(input :String) -> String:\n\treturn input.replace(\"\\r\\n\", \"\\n\")\n\n\nclass Token extends RefCounted:\n\tvar _token: String\n\tvar _consumed: int\n\tvar _is_operator: bool\n\tvar _regex :RegEx\n\n\n\tfunc _init(p_token: String, p_is_operator := false, p_regex :RegEx = null) -> void:\n\t\t_token = p_token\n\t\t_is_operator = p_is_operator\n\t\t_consumed = p_token.length()\n\t\t_regex = p_regex\n\n\tfunc match(input: String, pos: int) -> bool:\n\t\tif _regex:\n\t\t\tvar result := _regex.search(input, pos)\n\t\t\tif result == null:\n\t\t\t\treturn false\n\t\t\t_consumed = result.get_end() - result.get_start()\n\t\t\treturn pos == result.get_start()\n\t\treturn input.findn(_token, pos) == pos\n\n\tfunc is_operator() -> bool:\n\t\treturn _is_operator\n\n\tfunc is_inner_class() -> bool:\n\t\treturn _token == \"class\"\n\n\tfunc is_variable() -> bool:\n\t\treturn false\n\n\tfunc is_token(token_name :String) -> bool:\n\t\treturn _token == token_name\n\n\tfunc is_skippable() -> bool:\n\t\treturn false\n\n\tfunc _to_string() -> String:\n\t\treturn \"Token{\" + _token + \"}\"\n\n\nclass Operator extends Token:\n\tfunc _init(value: String) -> void:\n\t\tsuper(value, true)\n\n\tfunc _to_string() -> String:\n\t\treturn \"OperatorToken{%s}\" % [_token]\n\n\n# A skippable token, is just a placeholder like space or tabs\nclass SkippableToken extends Token:\n\n\tfunc _init(p_token: String) -> void:\n\t\tsuper(p_token)\n\n\tfunc is_skippable() -> bool:\n\t\treturn true\n\n\n# Token to parse Fuzzers\nclass FuzzerToken extends Token:\n\tvar _name: String\n\n\n\tfunc _init(regex: RegEx) -> void:\n\t\tsuper(\"\", false, regex)\n\n\n\tfunc match(input: String, pos: int) -> bool:\n\t\tif _regex:\n\t\t\tvar result := _regex.search(input, pos)\n\t\t\tif result == null:\n\t\t\t\treturn false\n\t\t\t_name = result.strings[1]\n\t\t\t_consumed = result.get_end() - result.get_start()\n\t\t\treturn pos == result.get_start()\n\t\treturn input.findn(_token, pos) == pos\n\n\n\tfunc name() -> String:\n\t\treturn _name\n\n\n\tfunc type() -> int:\n\t\treturn GdObjects.TYPE_FUZZER\n\n\n\tfunc _to_string() -> String:\n\t\treturn \"FuzzerToken{%s: '%s'}\" % [_name, _token]\n\n\n# Token to parse function arguments\nclass Variable extends Token:\n\tvar _plain_value :String\n\tvar _typed_value :Variant\n\tvar _type :int = TYPE_NIL\n\n\n\tfunc _init(p_value: String) -> void:\n\t\tsuper(p_value)\n\t\t_type = _scan_type(p_value)\n\t\t_plain_value = p_value\n\t\t_typed_value = _cast_to_type(p_value, _type)\n\n\n\tfunc _scan_type(p_value: String) -> int:\n\t\tif p_value.begins_with(\"\\\"\") and p_value.ends_with(\"\\\"\"):\n\t\t\treturn TYPE_STRING\n\t\tvar type_ := GdObjects.string_to_type(p_value)\n\t\tif type_ != TYPE_NIL:\n\t\t\treturn type_\n\t\tif p_value.is_valid_int():\n\t\t\treturn TYPE_INT\n\t\tif p_value.is_valid_float():\n\t\t\treturn TYPE_FLOAT\n\t\tif p_value.is_valid_hex_number():\n\t\t\treturn TYPE_INT\n\t\treturn TYPE_OBJECT\n\n\n\tfunc _cast_to_type(p_value :String, p_type: int) -> Variant:\n\t\tmatch p_type:\n\t\t\tTYPE_STRING:\n\t\t\t\treturn p_value#.substr(1, p_value.length() - 2)\n\t\t\tTYPE_INT:\n\t\t\t\treturn p_value.to_int()\n\t\t\tTYPE_FLOAT:\n\t\t\t\treturn p_value.to_float()\n\t\treturn p_value\n\n\n\tfunc is_variable() -> bool:\n\t\treturn true\n\n\n\tfunc type() -> int:\n\t\treturn _type\n\n\n\tfunc value() -> Variant:\n\t\treturn _typed_value\n\n\n\tfunc plain_value() -> String:\n\t\treturn _plain_value\n\n\n\tfunc _to_string() -> String:\n\t\treturn \"Variable{%s: %s : '%s'}\" % [_plain_value, GdObjects.type_as_string(_type), _token]\n\n\nclass TokenInnerClass extends Token:\n\tvar _clazz_name :String\n\tvar _content := PackedStringArray()\n\n\n\tstatic func _strip_leading_spaces(input :String) -> String:\n\t\tvar characters := input.to_utf8_buffer()\n\t\twhile not characters.is_empty():\n\t\t\tif characters[0] != 0x20:\n\t\t\t\tbreak\n\t\t\tcharacters.remove_at(0)\n\t\treturn characters.get_string_from_utf8()\n\n\n\tstatic func _consumed_bytes(row :String) -> int:\n\t\treturn row.replace(\" \", \"\").replace(\"\t\", \"\").length()\n\n\n\tfunc _init(clazz_name :String) -> void:\n\t\tsuper(\"class\")\n\t\t_clazz_name = clazz_name\n\n\n\tfunc is_class_name(clazz_name :String) -> bool:\n\t\treturn _clazz_name == clazz_name\n\n\n\tfunc content() -> PackedStringArray:\n\t\treturn _content\n\n\n\tfunc parse(source_rows :PackedStringArray, offset :int) -> void:\n\t\t# add class signature\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\t_content.append(source_rows[offset])\n\t\t# parse class content\n\t\tfor row_index in range(offset+1, source_rows.size()):\n\t\t\t# scan until next non tab\n\t\t\tvar source_row := source_rows[row_index]\n\t\t\tvar row := TokenInnerClass._strip_leading_spaces(source_row)\n\t\t\tif row.is_empty() or row.begins_with(\"\\t\") or row.begins_with(\"#\"):\n\t\t\t\t# fold all line to left by removing leading tabs and spaces\n\t\t\t\tif source_row.begins_with(\"\\t\"):\n\t\t\t\t\tsource_row = source_row.trim_prefix(\"\\t\")\n\t\t\t\t# refomat invalid empty lines\n\t\t\t\tif source_row.dedent().is_empty():\n\t\t\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t\t\t_content.append(\"\")\n\t\t\t\telse:\n\t\t\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t\t\t_content.append(source_row)\n\t\t\t\tcontinue\n\t\t\tbreak\n\t\t_consumed += TokenInnerClass._consumed_bytes(\"\".join(_content))\n\n\n\tfunc _to_string() -> String:\n\t\treturn \"TokenInnerClass{%s}\" % [_clazz_name]\n\n\n\nfunc get_token(input :String, current_index :int) -> Token:\n\tfor t in TOKENS:\n\t\tif t.match(input, current_index):\n\t\t\treturn t\n\treturn TOKEN_NOT_MATCH\n\n\nfunc next_token(input: String, current_index: int, ignore_tokens :Array[Token] = []) -> Token:\n\tvar token := TOKEN_NOT_MATCH\n\tfor t :Token in TOKENS.filter(func(t :Token) -> bool: return not ignore_tokens.has(t)):\n\t\tif t.match(input, current_index):\n\t\t\ttoken = t\n\t\t\tbreak\n\tif token == OPERATOR_SUB:\n\t\ttoken = tokenize_value(input, current_index, token)\n\tif token == TOKEN_INNER_CLASS:\n\t\ttoken = tokenize_inner_class(input, current_index, token)\n\tif token == TOKEN_NOT_MATCH:\n\t\treturn tokenize_value(input, current_index, token, ignore_tokens.has(TOKEN_FUNCTION))\n\treturn token\n\n\nfunc tokenize_value(input: String, current: int, token: Token, ignore_dots := false) -> Token:\n\tvar next := 0\n\tvar current_token := \"\"\n\t# test for '--', '+-', '*-', '/-', '%-', or at least '-x'\n\tvar test_for_sign := (token == null or token.is_operator()) and input[current] == \"-\"\n\twhile current + next < len(input):\n\t\tvar character := input[current + next] as String\n\t\t# if first charater a sign\n\t\t# or allowend charset\n\t\t# or is a float value\n\t\tif (test_for_sign and next==0) \\\n\t\t\tor character in ALLOWED_CHARACTERS \\\n\t\t\tor (character == \".\" and (ignore_dots or current_token.is_valid_int())):\n\t\t\tcurrent_token += character\n\t\t\tnext += 1\n\t\t\tcontinue\n\t\tbreak\n\tif current_token != \"\":\n\t\treturn Variable.new(current_token)\n\treturn TOKEN_NOT_MATCH\n\n\nfunc extract_clazz_name(value :String) -> String:\n\tvar result := _regex_clazz_name.search(value)\n\tif result == null:\n\t\tpush_error(\"Can't extract class name from '%s'\" % value)\n\t\treturn \"\"\n\tif result.get_string(2).is_empty():\n\t\treturn result.get_string(5)\n\telse:\n\t\treturn result.get_string(2)\n\n\n@warning_ignore(\"unused_parameter\")\nfunc tokenize_inner_class(source_code: String, current: int, token: Token) -> Token:\n\tvar clazz_name := extract_clazz_name(source_code.substr(current, 64))\n\treturn TokenInnerClass.new(clazz_name)\n\n\nfunc parse_return_token(input: String) -> Variable:\n\tvar index := input.rfind(TOKEN_FUNCTION_RETURN_TYPE._token)\n\tif index == -1:\n\t\treturn TOKEN_NOT_MATCH\n\tindex += TOKEN_FUNCTION_RETURN_TYPE._consumed\n\t# We scan for the return value exclusive '.' token because it could be referenced to a\n\t# external or internal class e.g.  'func foo() -> InnerClass.Bar:'\n\tvar token := next_token(input, index, [TOKEN_FUNCTION])\n\twhile !token.is_variable() and token != TOKEN_NOT_MATCH:\n\t\tindex += token._consumed\n\t\ttoken = next_token(input, index, [TOKEN_FUNCTION])\n\treturn token\n\n\nfunc get_function_descriptors(script: GDScript, included_functions: PackedStringArray = []) -> Array[GdFunctionDescriptor]:\n\tvar fds: Array[GdFunctionDescriptor] = []\n\tfor method_descriptor in script.get_script_method_list():\n\t\tvar func_name: String = method_descriptor[\"name\"]\n\t\tif included_functions.is_empty() or func_name in included_functions:\n\t\t\t# exclude type set/geters\n\t\t\tif is_getter_or_setter(func_name):\n\t\t\t\tcontinue\n\t\t\tif not fds.any(func(fd: GdFunctionDescriptor) -> bool: return fd.name() == func_name):\n\t\t\t\tfds.append(GdFunctionDescriptor.extract_from(method_descriptor, false))\n\n\t# we need to enrich it by default arguments and line number by parsing the script\n\t# the engine core functions has no valid methods to get this info\n\t_prescan_script(script)\n\t_enrich_function_descriptor(script, fds)\n\treturn fds\n\n\nfunc is_getter_or_setter(func_name: String) -> bool:\n\treturn func_name.begins_with(\"@\") and (func_name.ends_with(\"getter\") or func_name.ends_with(\"setter\"))\n\n\nfunc _parse_function_arguments(input: String) -> Dictionary:\n\tvar arguments := {}\n\tvar current_index := 0\n\tvar token :Token = null\n\tvar bracket := 0\n\tvar in_function := false\n\twhile current_index < len(input):\n\t\ttoken = next_token(input, current_index)\n\t\t# fallback to not end in a endless loop\n\t\tif token == TOKEN_NOT_MATCH:\n\t\t\tvar error : = \"\"\"\n\t\t\t\tParsing Error: Invalid token at pos %d found.\n\t\t\t\tPlease report this error!\n\t\t\t\tsource_code:\n\t\t\t\t--------------------------------------------------------------\n\t\t\t\t%s\n\t\t\t\t--------------------------------------------------------------\n\t\t\t\"\"\".dedent() % [current_index, input]\n\t\t\tpush_error(error)\n\t\t\tcurrent_index += 1\n\t\t\tcontinue\n\t\tcurrent_index += token._consumed\n\t\tif token.is_skippable():\n\t\t\tcontinue\n\t\tif token == TOKEN_BRACKET_OPEN:\n\t\t\tin_function = true\n\t\t\tbracket += 1\n\t\t\tcontinue\n\t\tif token == TOKEN_BRACKET_CLOSE:\n\t\t\tbracket -= 1\n\t\t# if function end?\n\t\tif in_function and bracket == 0:\n\t\t\treturn arguments\n\t\t# is function\n\t\tif token == TOKEN_FUNCTION_DECLARATION:\n\t\t\ttoken = next_token(input, current_index)\n\t\t\tcurrent_index += token._consumed\n\t\t\tcontinue\n\t\t# is fuzzer argument\n\t\tif token is FuzzerToken:\n\t\t\tvar arg_value := _parse_end_function(input.substr(current_index), true)\n\t\t\tcurrent_index += arg_value.length()\n\t\t\tvar arg_name :String = (token as FuzzerToken).name()\n\t\t\targuments[arg_name] = arg_value.lstrip(\" \")\n\t\t\tcontinue\n\t\t# is value argument\n\t\tif in_function and token.is_variable():\n\t\t\tvar arg_name: String = (token as Variable).plain_value()\n\t\t\tvar arg_value: String = GdFunctionArgument.UNDEFINED\n\t\t\t# parse type and default value\n\t\t\twhile current_index < len(input):\n\t\t\t\ttoken = next_token(input, current_index)\n\t\t\t\tcurrent_index += token._consumed\n\t\t\t\tif token.is_skippable():\n\t\t\t\t\tcontinue\n\t\t\t\tmatch token:\n\t\t\t\t\tTOKEN_ARGUMENT_TYPE:\n\t\t\t\t\t\ttoken = next_token(input, current_index)\n\t\t\t\t\t\tif token == TOKEN_SPACE:\n\t\t\t\t\t\t\tcurrent_index += token._consumed\n\t\t\t\t\t\t\ttoken = next_token(input, current_index)\n\t\t\t\t\tTOKEN_ARGUMENT_TYPE_ASIGNMENT:\n\t\t\t\t\t\targ_value = _parse_end_function(input.substr(current_index), true)\n\t\t\t\t\t\tcurrent_index += arg_value.length()\n\t\t\t\t\tTOKEN_ARGUMENT_ASIGNMENT:\n\t\t\t\t\t\ttoken = next_token(input, current_index)\n\t\t\t\t\t\targ_value = _parse_end_function(input.substr(current_index), true)\n\t\t\t\t\t\tcurrent_index += arg_value.length()\n\t\t\t\t\tTOKEN_BRACKET_OPEN:\n\t\t\t\t\t\tbracket += 1\n\t\t\t\t\t\t# if value a function?\n\t\t\t\t\t\tif bracket > 1:\n\t\t\t\t\t\t\t# complete the argument value\n\t\t\t\t\t\t\tvar func_begin := input.substr(current_index-TOKEN_BRACKET_OPEN._consumed)\n\t\t\t\t\t\t\tvar func_body := _parse_end_function(func_begin)\n\t\t\t\t\t\t\targ_value += func_body\n\t\t\t\t\t\t\t# fix parse index to end of value\n\t\t\t\t\t\t\tcurrent_index += func_body.length() - TOKEN_BRACKET_OPEN._consumed - TOKEN_BRACKET_CLOSE._consumed\n\t\t\t\t\tTOKEN_BRACKET_CLOSE:\n\t\t\t\t\t\tbracket -= 1\n\t\t\t\t\t\t# end of function\n\t\t\t\t\t\tif bracket == 0:\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\tTOKEN_ARGUMENT_SEPARATOR:\n\t\t\t\t\t\tif bracket <= 1:\n\t\t\t\t\t\t\tbreak\n\t\t\targuments[arg_name] = arg_value.lstrip(\" \")\n\treturn arguments\n\n\nfunc _parse_end_function(input: String, remove_trailing_char := false) -> String:\n\t# find end of function\n\tvar current_index := 0\n\tvar bracket_count := 0\n\tvar in_array := 0\n\tvar end_of_func := false\n\n\twhile current_index < len(input) and not end_of_func:\n\t\tvar character := input[current_index]\n\t\t# step over strings\n\t\tif character == \"'\" :\n\t\t\tcurrent_index = input.find(\"'\", current_index+1) + 1\n\t\t\tif current_index == 0:\n\t\t\t\tpush_error(\"Parsing error on '%s', can't evaluate end of string.\" % input)\n\t\t\t\treturn \"\"\n\t\t\tcontinue\n\t\tif character == '\"' :\n\t\t\t# test for string blocks\n\t\t\tif input.find('\"\"\"', current_index) == current_index:\n\t\t\t\tcurrent_index = input.find('\"\"\"', current_index+3) + 3\n\t\t\telse:\n\t\t\t\tcurrent_index = input.find('\"', current_index+1) + 1\n\t\t\tif current_index == 0:\n\t\t\t\tpush_error(\"Parsing error on '%s', can't evaluate end of string.\" % input)\n\t\t\t\treturn \"\"\n\t\t\tcontinue\n\n\t\tmatch character:\n\t\t\t# count if inside an array\n\t\t\t\"[\": in_array += 1\n\t\t\t\"]\": in_array -= 1\n\t\t\t# count if inside a function\n\t\t\t\"(\": bracket_count += 1\n\t\t\t\")\":\n\t\t\t\tbracket_count -= 1\n\t\t\t\tif bracket_count < 0 and in_array <= 0:\n\t\t\t\t\tend_of_func = true\n\t\t\t\",\":\n\t\t\t\tif bracket_count == 0 and in_array == 0:\n\t\t\t\t\tend_of_func = true\n\t\tcurrent_index += 1\n\tif remove_trailing_char:\n\t\t# check if the parsed value ends with comma or end of doubled breaked\n\t\t# `<value>,` or `<function>())`\n\t\tvar trailing_char := input[current_index-1]\n\t\tif trailing_char == ',' or (bracket_count < 0 and trailing_char == ')'):\n\t\t\treturn input.substr(0, current_index-1)\n\treturn input.substr(0, current_index)\n\n\n@warning_ignore(\"unsafe_method_access\")\nfunc extract_inner_class(source_rows: PackedStringArray, clazz_name :String) -> PackedStringArray:\n\tfor row_index in source_rows.size():\n\t\tvar input := source_rows[row_index]\n\t\tvar token := next_token(input, 0)\n\t\tif token.is_inner_class():\n\t\t\tif token.is_class_name(clazz_name):\n\t\t\t\ttoken.parse(source_rows, row_index)\n\t\t\t\treturn token.content()\n\treturn PackedStringArray()\n\n\nfunc extract_func_signature(rows :PackedStringArray, index :int) -> String:\n\tvar signature := \"\"\n\n\tfor rowIndex in range(index, rows.size()):\n\t\tvar row := rows[rowIndex]\n\t\trow = _regex_strip_comments.sub(row, \"\").strip_edges(false)\n\t\tif row.is_empty():\n\t\t\tcontinue\n\t\tsignature += row + \"\\n\"\n\t\tif is_func_end(row):\n\t\t\treturn signature.strip_edges()\n\tpush_error(\"Can't fully extract function signature of '%s'\" % rows[index])\n\treturn \"\"\n\n\nfunc get_class_name(script :GDScript) -> String:\n\tvar source_code := GdScriptParser.to_unix_format(script.source_code)\n\tvar source_rows := source_code.split(\"\\n\")\n\n\tfor index :int in min(10, source_rows.size()):\n\t\tvar input := source_rows[index]\n\t\tvar token := next_token(input, 0)\n\t\tif token == TOKEN_CLASS_NAME:\n\t\t\tvar current_index := token._consumed\n\t\t\ttoken = next_token(input, current_index)\n\t\t\tcurrent_index += token._consumed\n\t\t\ttoken = tokenize_value(input, current_index, token)\n\t\t\treturn (token as Variable).value()\n\t# if no class_name found extract from file name\n\treturn GdObjects.to_pascal_case(script.resource_path.get_basename().get_file())\n\n\nfunc parse_func_name(input :String) -> String:\n\tvar current_index := 0\n\tvar token := next_token(input, current_index)\n\tcurrent_index += token._consumed\n\tif token != TOKEN_FUNCTION_STATIC_DECLARATION and token != TOKEN_FUNCTION_DECLARATION:\n\t\treturn \"\"\n\twhile not token is Variable:\n\t\ttoken = next_token(input, current_index)\n\t\tcurrent_index += token._consumed\n\treturn token._token\n\n\n## Enriches the function descriptor by line number and argument default values\n## - enrich all function descriptors form current script up to all inherited scrips\nfunc _enrich_function_descriptor(script: GDScript, fds: Array[GdFunctionDescriptor]) -> void:\n\tvar enriched_functions := PackedStringArray()\n\tvar script_to_scan := script\n\twhile script_to_scan != null:\n\t\t# do not scan the test suite base class itself\n\t\tif script_to_scan.resource_path == \"res://addons/gdUnit4/src/GdUnitTestSuite.gd\":\n\t\t\tbreak\n\n\t\tvar rows := script_to_scan.source_code.split(\"\\n\")\n\t\tfor rowIndex in rows.size():\n\t\t\tvar input := rows[rowIndex]\n\t\t\t# step over inner class functions\n\t\t\tif input.begins_with(\"\\t\"):\n\t\t\t\tcontinue\n\t\t\t# skip comments and empty lines\n\t\t\tif input.begins_with(\"#\") or input.length() == 0:\n\t\t\t\tcontinue\n\t\t\tvar token := next_token(input, 0)\n\t\t\tif token == TOKEN_FUNCTION_STATIC_DECLARATION or token == TOKEN_FUNCTION_DECLARATION:\n\t\t\t\tvar function_name := parse_func_name(input)\n\t\t\t\tvar fd: GdFunctionDescriptor = fds.filter(func(element: GdFunctionDescriptor) -> bool:\n\t\t\t\t\t# is same function name and not already enriched\n\t\t\t\t\treturn function_name == element.name() and not enriched_functions.has(element.name())\n\t\t\t\t).pop_front()\n\t\t\t\tif fd != null:\n\t\t\t\t\t# add as enriched function to exclude from next iteration (could be inherited)\n\t\t\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t\t\tenriched_functions.append(fd.name())\n\t\t\t\t\tvar func_signature := extract_func_signature(rows, rowIndex)\n\t\t\t\t\tvar func_arguments := _parse_function_arguments(func_signature)\n\t\t\t\t\t# enrich missing default values\n\t\t\t\t\tfor arg_name: String in func_arguments.keys():\n\t\t\t\t\t\tvar func_argument: String = func_arguments[arg_name]\n\t\t\t\t\t\tfd.set_argument_value(arg_name, func_argument)\n\t\t\t\t\tfd.enrich_file_info(script_to_scan.resource_path, rowIndex + 1)\n\t\t\t\t\tfd._is_coroutine = is_func_coroutine(rows, rowIndex)\n\t\t\t\t\t# enrich return class name if not set\n\t\t\t\t\tif fd.return_type() == TYPE_OBJECT and fd._return_class in [\"\", \"Resource\", \"RefCounted\"]:\n\t\t\t\t\t\tvar var_token := parse_return_token(func_signature)\n\t\t\t\t\t\tif var_token != TOKEN_NOT_MATCH and var_token.type() == TYPE_OBJECT:\n\t\t\t\t\t\t\tfd._return_class = _patch_inner_class_names(var_token.plain_value(), \"\")\n\t\t# if the script ihnerits we need to scan this also\n\t\tscript_to_scan = script_to_scan.get_base_script()\n\n\nfunc is_func_coroutine(rows :PackedStringArray, index :int) -> bool:\n\tvar is_coroutine := false\n\tfor rowIndex in range(index+1, rows.size()):\n\t\tvar input := rows[rowIndex]\n\t\tis_coroutine = input.contains(\"await\")\n\t\tif is_coroutine:\n\t\t\treturn true\n\t\tvar token := next_token(input, 0)\n\t\t# scan until next function\n\t\tif token == TOKEN_FUNCTION_STATIC_DECLARATION or token == TOKEN_FUNCTION_DECLARATION:\n\t\t\tbreak\n\treturn is_coroutine\n\n\nfunc is_inner_class(clazz_path :PackedStringArray) -> bool:\n\treturn clazz_path.size() > 1\n\n\nfunc is_func_end(row :String) -> bool:\n\treturn row.strip_edges(false, true).ends_with(\":\")\n\n\nfunc _patch_inner_class_names(clazz :String, clazz_name :String = \"\") -> String:\n\tvar inner_clazz_name := clazz.split(\".\")[0]\n\tif _scanned_inner_classes.has(inner_clazz_name):\n\t\treturn inner_clazz_name\n\t\t#var base_clazz := clazz_name.split(\".\")[0]\n\t\t#return base_clazz + \".\" + clazz\n\tif _script_constants.has(clazz):\n\t\treturn clazz_name + \".\" + clazz\n\treturn clazz\n\n\nfunc _prescan_script(script: GDScript) -> void:\n\t_script_constants = script.get_script_constant_map()\n\tfor key :String in _script_constants.keys():\n\t\tvar value :Variant = _script_constants.get(key)\n\t\tif value is GDScript:\n\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t_scanned_inner_classes.append(key)\n\n\nfunc parse(clazz_name :String, clazz_path :PackedStringArray) -> GdUnitResult:\n\tif clazz_path.is_empty():\n\t\treturn GdUnitResult.error(\"Invalid script path '%s'\" % clazz_path)\n\tvar is_inner_class_ := is_inner_class(clazz_path)\n\tvar script :GDScript = load(clazz_path[0])\n\t_prescan_script(script)\n\n\tif is_inner_class_:\n\t\tvar inner_class_name := clazz_path[1]\n\t\tif _scanned_inner_classes.has(inner_class_name):\n\t\t\t# do load only on inner class source code and enrich the stored script instance\n\t\t\tvar source_code := _load_inner_class(script, inner_class_name)\n\t\t\tscript = _script_constants.get(inner_class_name)\n\t\t\tscript.source_code = source_code\n\tvar function_descriptors := get_function_descriptors(script)\n\tvar gd_class := GdClassDescriptor.new(clazz_name, is_inner_class_, function_descriptors)\n\treturn GdUnitResult.success(gd_class)\n\n\nfunc _load_inner_class(script: GDScript, inner_clazz: String) -> String:\n\tvar source_rows := GdScriptParser.to_unix_format(script.source_code).split(\"\\n\")\n\t# extract all inner class names\n\tvar inner_class_code := extract_inner_class(source_rows, inner_clazz)\n\treturn \"\\n\".join(inner_class_code)\n"
  },
  {
    "path": "addons/gdUnit4/src/core/parse/GdScriptParser.gd.uid",
    "content": "uid://br6f6ywnvq2cg\n"
  },
  {
    "path": "addons/gdUnit4/src/core/parse/GdUnitExpressionRunner.gd",
    "content": "class_name GdUnitExpressionRunner\nextends RefCounted\n\nconst CLASS_TEMPLATE = \"\"\"\nclass_name _ExpressionRunner extends '${clazz_path}'\n\nfunc __run_expression() -> Variant:\n\treturn $expression\n\n\"\"\"\n\nvar constructor_args_regex := RegEx.create_from_string(\"new\\\\((?<args>.*)\\\\)\")\n\n\nfunc execute(src_script: GDScript, value: Variant) -> Variant:\n\tif typeof(value) != TYPE_STRING:\n\t\treturn value\n\n\tvar expression: String = value\n\tvar parameter_map := src_script.get_script_constant_map()\n\tfor key: String in parameter_map.keys():\n\t\tvar parameter_value: Variant = parameter_map[key]\n\t\t# check we need to construct from inner class\n\t\t# we need to use the original class instance from the script_constant_map otherwise we run into a runtime error\n\t\tif expression.begins_with(key + \".new\") and parameter_value is GDScript:\n\t\t\tvar object: GDScript = parameter_value\n\t\t\tvar args := build_constructor_arguments(parameter_map, expression.substr(expression.find(\"new\")))\n\t\t\tif args.is_empty():\n\t\t\t\treturn object.new()\n\t\t\treturn object.callv(\"new\", args)\n\n\tvar script := GDScript.new()\n\tvar resource_path := \"res://addons/gdUnit4/src/Fuzzers.gd\" if src_script.resource_path.is_empty() else src_script.resource_path\n\tscript.source_code = CLASS_TEMPLATE.dedent()\\\n\t\t.replace(\"${clazz_path}\", resource_path)\\\n\t\t.replace(\"$expression\", expression)\n\t#script.take_over_path(resource_path)\n\t@warning_ignore(\"return_value_discarded\")\n\tscript.reload(true)\n\tvar runner: Object = script.new()\n\tif runner.has_method(\"queue_free\"):\n\t\t(runner as Node).queue_free()\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn runner.__run_expression()\n\n\nfunc build_constructor_arguments(parameter_map: Dictionary, expression: String) -> Array[Variant]:\n\tvar result := constructor_args_regex.search(expression)\n\tvar extracted_arguments := result.get_string(\"args\").strip_edges()\n\tif extracted_arguments.is_empty():\n\t\treturn []\n\tvar arguments :Array = extracted_arguments.split(\",\")\n\treturn arguments.map(func(argument: String) -> Variant:\n\t\tvar value := argument.strip_edges()\n\n\t\t# is argument an constant value\n\t\tif parameter_map.has(value):\n\t\t\treturn parameter_map[value]\n\t\t# is typed named value like Vector3.ONE\n\t\tfor type:int in GdObjects.TYPE_AS_STRING_MAPPINGS:\n\t\t\tvar type_as_string:String = GdObjects.TYPE_AS_STRING_MAPPINGS[type]\n\t\t\tif value.begins_with(type_as_string):\n\t\t\t\treturn type_convert(value, type)\n\t\t# is value a string\n\t\tif value.begins_with(\"'\") or value.begins_with('\"'):\n\t\t\treturn value.trim_prefix(\"'\").trim_suffix(\"'\").trim_prefix('\"').trim_suffix('\"')\n\t\t# fallback to default value converting\n\t\treturn str_to_var(value)\n\t)\n\n\nfunc to_fuzzer(src_script: GDScript, expression: String) -> Fuzzer:\n\t@warning_ignore(\"unsafe_cast\")\n\treturn execute(src_script, expression) as Fuzzer\n"
  },
  {
    "path": "addons/gdUnit4/src/core/parse/GdUnitExpressionRunner.gd.uid",
    "content": "uid://c3iionhahewlf\n"
  },
  {
    "path": "addons/gdUnit4/src/core/parse/GdUnitTestParameterSetResolver.gd",
    "content": "class_name GdUnitTestParameterSetResolver\nextends RefCounted\n\nconst CLASS_TEMPLATE = \"\"\"\nclass_name _ParameterExtractor extends '${clazz_path}'\n\nfunc __extract_test_parameters() -> Array:\n\treturn ${test_params}\n\n\"\"\"\n\nconst EXCLUDE_PROPERTIES_TO_COPY = [\n\t\"script\",\n\t\"type\",\n\t\"Node\",\n\t\"_import_path\"]\n\n\nvar _fd: GdFunctionDescriptor\nvar _test_case_names_cache := PackedStringArray()\nvar _static_sets_by_index := {}\nvar _is_static := true\n\nfunc _init(fd: GdFunctionDescriptor) -> void:\n\t_fd = fd\n\n\nfunc is_parameterized() -> bool:\n\treturn _fd.is_parameterized()\n\n\nfunc is_parameter_sets_static() -> bool:\n\treturn _is_static\n\n\nfunc is_parameter_set_static(index: int) -> bool:\n\treturn _is_static and _static_sets_by_index.get(index, false)\n\n\n# validates the given arguments are complete and matches to required input fields of the test function\nfunc validate(input_value_set: Array) -> String:\n\tvar input_arguments := _fd.args()\n\t# check given parameter set with test case arguments\n\tvar expected_arg_count := input_arguments.size() - 1\n\tfor input_values :Variant in input_value_set:\n\t\tvar parameter_set_index := input_value_set.find(input_values)\n\t\tif input_values is Array:\n\t\t\tvar arr_values: Array = input_values\n\t\t\tvar current_arg_count := arr_values.size()\n\t\t\tif current_arg_count != expected_arg_count:\n\t\t\t\treturn \"\\n\tThe parameter set at index [%d] does not match the expected input parameters!\\n\tThe test case requires [%d] input parameters, but the set contains [%d]\" % [parameter_set_index, expected_arg_count, current_arg_count]\n\t\t\tvar error := GdUnitTestParameterSetResolver.validate_parameter_types(input_arguments, arr_values, parameter_set_index)\n\t\t\tif not error.is_empty():\n\t\t\t\treturn error\n\t\telse:\n\t\t\treturn \"\\n\tThe parameter set at index [%d] does not match the expected input parameters!\\n\tExpecting an array of input values.\" % parameter_set_index\n\treturn \"\"\n\n\nstatic func validate_parameter_types(input_arguments: Array, input_values: Array, parameter_set_index: int) -> String:\n\tfor i in input_arguments.size():\n\t\tvar input_param: GdFunctionArgument = input_arguments[i]\n\t\t# only check the test input arguments\n\t\tif input_param.is_parameter_set():\n\t\t\tcontinue\n\t\tvar input_param_type := input_param.type()\n\t\tvar input_value :Variant = input_values[i]\n\t\tvar input_value_type := typeof(input_value)\n\t\t# input parameter is not typed or is Variant we skip the type test\n\t\tif input_param_type == TYPE_NIL or input_param_type == GdObjects.TYPE_VARIANT:\n\t\t\tcontinue\n\t\t# is input type enum allow int values\n\t\tif input_param_type == GdObjects.TYPE_VARIANT and input_value_type == TYPE_INT:\n\t\t\tcontinue\n\t\t# allow only equal types and object == null\n\t\tif input_param_type == TYPE_OBJECT and input_value_type == TYPE_NIL:\n\t\t\tcontinue\n\t\tif input_param_type != input_value_type:\n\t\t\treturn \"\\n\tThe parameter set at index [%d] does not match the expected input parameters!\\n\tThe value '%s' does not match the required input parameter <%s>.\" % [parameter_set_index, input_value, input_param]\n\treturn \"\"\n\n\nfunc build_test_case_names(test_case: _TestCase) -> PackedStringArray:\n\tif not is_parameterized():\n\t\treturn []\n\t# if test names already resolved?\n\tif not _test_case_names_cache.is_empty():\n\t\treturn _test_case_names_cache\n\n\tvar fa := GdFunctionArgument.get_parameter_set(_fd.args())\n\tvar parameter_sets := fa.parameter_sets()\n\t# if no parameter set detected we need to resolve it by using reflection\n\tif parameter_sets.size() == 0:\n\t\t_test_case_names_cache = _extract_test_names_by_reflection(test_case)\n\t\t_is_static = false\n\telse:\n\t\tvar property_names := _extract_property_names(test_case.get_parent())\n\t\tfor parameter_set_index in parameter_sets.size():\n\t\t\tvar parameter_set := parameter_sets[parameter_set_index]\n\t\t\t_static_sets_by_index[parameter_set_index] = _is_static_parameter_set(parameter_set, property_names)\n\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t_test_case_names_cache.append(GdUnitTestParameterSetResolver._build_test_case_name(test_case, parameter_set, parameter_set_index))\n\t\t\tparameter_set_index += 1\n\treturn _test_case_names_cache\n\n\nfunc _extract_property_names(node :Node) -> PackedStringArray:\n\treturn node.get_property_list()\\\n\t\t.map(func(property :Dictionary) -> String: return property[\"name\"])\\\n\t\t.filter(func(property :String) -> bool: return !EXCLUDE_PROPERTIES_TO_COPY.has(property))\n\n\n# tests if the test property set contains an property reference by name, if not the parameter set holds only static values\nfunc _is_static_parameter_set(parameters :String, property_names :PackedStringArray) -> bool:\n\tfor property_name in property_names:\n\t\tif parameters.contains(property_name):\n\t\t\t_is_static = false\n\t\t\treturn false\n\treturn true\n\n\nfunc _extract_test_names_by_reflection(test_case: _TestCase) -> PackedStringArray:\n\tvar parameter_sets := load_parameter_sets(test_case)\n\tvar test_case_names: PackedStringArray = []\n\tfor index in parameter_sets.size():\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\ttest_case_names.append(GdUnitTestParameterSetResolver._build_test_case_name(test_case, str(parameter_sets[index]), index))\n\treturn test_case_names\n\n\nstatic func _build_test_case_name(test_case: _TestCase, test_parameter: String, parameter_set_index: int) -> String:\n\tif not test_parameter.begins_with(\"[\"):\n\t\ttest_parameter = \"[\" + test_parameter\n\treturn \"%s:%d %s\" % [test_case.get_name(), parameter_set_index, test_parameter.replace(\"\\t\", \"\").replace('\"', \"'\").replace(\"&'\", \"'\")]\n\n\n# extracts the arguments from the given test case, using kind of reflection solution\n# to restore the parameters from a string representation to real instance type\nfunc load_parameter_sets(test_case: _TestCase, do_validate := false) -> Array:\n\tvar source_script :Script = test_case.get_parent().get_script()\n\tvar parameter_arg := GdFunctionArgument.get_parameter_set(_fd.args())\n\tvar source_code := CLASS_TEMPLATE \\\n\t\t.replace(\"${clazz_path}\", source_script.resource_path) \\\n\t\t.replace(\"${test_params}\", parameter_arg.value_as_string())\n\tvar script := GDScript.new()\n\tscript.source_code = source_code\n\t# enable this lines only for debuging\n\t#script.resource_path = GdUnitFileAccess.create_temp_dir(\"parameter_extract\") + \"/%s__.gd\" % test_case.get_name()\n\t#DirAccess.remove_absolute(script.resource_path)\n\t#ResourceSaver.save(script, script.resource_path)\n\tvar result := script.reload()\n\tif result != OK:\n\t\tpush_error(\"Extracting test parameters failed! Script loading error: %s\" % result)\n\t\treturn []\n\tvar instance :Object = script.new()\n\tGdUnitTestParameterSetResolver.copy_properties(test_case.get_parent(), instance)\n\t(instance as Node).queue_free()\n\tvar parameter_sets: Array = instance.call(\"__extract_test_parameters\")\n\tif not do_validate:\n\t\treturn parameter_sets\n\t# validate the parameter set\n\tvar error := validate(parameter_sets)\n\tif not error.is_empty():\n\t\ttest_case.skip(true, error)\n\t\ttest_case._interupted = true\n\tif parameter_sets.size() != _test_case_names_cache.size():\n\t\tpush_error(\"Internal Error: The resolved test_case names has invalid size!\")\n\t\terror = \"\"\"\n\t\t%s:\n\t\t\tThe resolved test_case names has invalid size!\n\t\t\t%s\n\t\t\"\"\".dedent().trim_prefix(\"\\n\") % [\n\t\t\tGdAssertMessages._error(\"Internal Error\"),\n\t\t\tGdAssertMessages._error(\"Please report this issue as a bug!\")]\n\t\tGdUnitThreadManager.get_current_context()\\\n\t\t\t.get_execution_context()\\\n\t\t\t.add_report(GdUnitReport.new().create(GdUnitReport.INTERUPTED, test_case.line_number(), error))\n\t\ttest_case.skip(true, error)\n\t\ttest_case._interupted = true\n\t@warning_ignore(\"return_value_discarded\")\n\tfixure_typed_parameters(parameter_sets, _fd.args())\n\treturn parameter_sets\n\n\nfunc fixure_typed_parameters(parameter_sets: Array, arg_descriptors: Array[GdFunctionArgument]) -> Array:\n\tfor parameter_set_index in parameter_sets.size():\n\t\tvar parameter_set: Array = parameter_sets[parameter_set_index]\n\t\t# run over all function arguments\n\t\tfor parameter_index in parameter_set.size():\n\t\t\tvar parameter :Variant = parameter_set[parameter_index]\n\t\t\tvar arg_descriptor: GdFunctionArgument = arg_descriptors[parameter_index]\n\t\t\tif parameter is Array:\n\t\t\t\tvar as_array: Array = parameter\n\t\t\t\t# we need to convert the untyped array to the expected typed version\n\t\t\t\tif arg_descriptor.is_typed_array():\n\t\t\t\t\tparameter_set[parameter_index] = Array(as_array, arg_descriptor.type_hint(), \"\", null)\n\treturn parameter_sets\n\n\n\nstatic func copy_properties(source: Object, dest: Object) -> void:\n\tfor property in source.get_property_list():\n\t\tvar property_name :String = property[\"name\"]\n\t\tvar property_value :Variant = source.get(property_name)\n\t\tif EXCLUDE_PROPERTIES_TO_COPY.has(property_name):\n\t\t\tcontinue\n\t\t#if dest.get(property_name) == null:\n\t\t#\tprints(\"|%s|\" % property_name, source.get(property_name))\n\n\t\t# check for invalid name property\n\t\tif property_name == \"name\" and property_value == \"\":\n\t\t\tdest.set(property_name, \"<empty>\");\n\t\t\tcontinue\n\t\tdest.set(property_name, property_value)\n"
  },
  {
    "path": "addons/gdUnit4/src/core/parse/GdUnitTestParameterSetResolver.gd.uid",
    "content": "uid://bur4rlbk4s0c4\n"
  },
  {
    "path": "addons/gdUnit4/src/core/report/GdUnitReport.gd",
    "content": "class_name GdUnitReport\nextends Resource\n\n# report type\nenum {\n\tSUCCESS,\n\tWARN,\n\tFAILURE,\n\tORPHAN,\n\tTERMINATED,\n\tINTERUPTED,\n\tABORT,\n\tSKIPPED,\n}\n\nvar _type :int\nvar _line_number :int\nvar _message :String\n\n\nfunc create(p_type :int, p_line_number :int, p_message :String) -> GdUnitReport:\n\t_type = p_type\n\t_line_number = p_line_number\n\t_message = p_message\n\treturn self\n\n\nfunc type() -> int:\n\treturn _type\n\n\nfunc line_number() -> int:\n\treturn _line_number\n\n\nfunc message() -> String:\n\treturn _message\n\n\nfunc is_skipped() -> bool:\n\treturn _type == SKIPPED\n\n\nfunc is_warning() -> bool:\n\treturn _type == WARN\n\n\nfunc is_failure() -> bool:\n\treturn _type == FAILURE\n\n\nfunc is_error() -> bool:\n\treturn _type == TERMINATED or _type == INTERUPTED or _type == ABORT\n\n\nfunc _to_string() -> String:\n\tif _line_number == -1:\n\t\treturn \"[color=green]line [/color][color=aqua]<n/a>:[/color] %s\" % [_message]\n\treturn \"[color=green]line [/color][color=aqua]%d:[/color] %s\" % [_line_number, _message]\n\n\nfunc serialize() -> Dictionary:\n\treturn {\n\t\t\"type\"        :_type,\n\t\t\"line_number\" :_line_number,\n\t\t\"message\"     :_message\n\t}\n\n\nfunc deserialize(serialized :Dictionary) -> GdUnitReport:\n\t_type        = serialized[\"type\"]\n\t_line_number = serialized[\"line_number\"]\n\t_message     = serialized[\"message\"]\n\treturn self\n"
  },
  {
    "path": "addons/gdUnit4/src/core/report/GdUnitReport.gd.uid",
    "content": "uid://vdpke4cc8k6b\n"
  },
  {
    "path": "addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteDefaultTemplate.gd",
    "content": "class_name GdUnitTestSuiteDefaultTemplate\nextends RefCounted\n\n\nconst DEFAULT_TEMP_TS_GD =\"\"\"\n\t# GdUnit generated TestSuite\n\tclass_name ${suite_class_name}\n\textends GdUnitTestSuite\n\t@warning_ignore('unused_parameter')\n\t@warning_ignore('return_value_discarded')\n\n\t# TestSuite generated from\n\tconst __source = '${source_resource_path}'\n\"\"\"\n\n\nconst DEFAULT_TEMP_TS_CS = \"\"\"\n\t// GdUnit generated TestSuite\n\n\tusing Godot;\n\tusing GdUnit4;\n\n\tnamespace ${name_space}\n\t{\n\t\tusing static Assertions;\n\t\tusing static Utils;\n\n\t\t[TestSuite]\n\t\tpublic class ${suite_class_name}\n\t\t{\n\t\t\t// TestSuite generated from\n\t\t\tprivate const string sourceClazzPath = \"${source_resource_path}\";\n\n\t\t}\n\t}\n\"\"\"\n"
  },
  {
    "path": "addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteDefaultTemplate.gd.uid",
    "content": "uid://c3i2e5yxeodwp\n"
  },
  {
    "path": "addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteTemplate.gd",
    "content": "class_name GdUnitTestSuiteTemplate\nextends RefCounted\n\nconst TEMPLATE_ID_GD = 1000\nconst TEMPLATE_ID_CS = 2000\n\nconst SUPPORTED_TAGS_GD = \"\"\"\n\tGdScript Tags are replaced when the test-suite is created.\n\n\t# The class name of the test-suite, formed from the source script.\n\t${suite_class_name}\n\t# is used to build the test suite class name\n\t\tclass_name ${suite_class_name}\n\t\textends GdUnitTestSuite\n\n\n\t# The class name in pascal case, formed from the source script.\n\t${source_class}\n\t\t# can be used to create the class e.g. for source 'MyClass'\n\t\tvar my_test_class := ${source_class}.new()\n\t\t# will be result in\n\t\tvar my_test_class := MyClass.new()\n\n\t# The class as variable name in snake case, formed from the source script.\n\t${source_var}\n\t\t# Can be used to build the variable name e.g. for source 'MyClass'\n\t\tvar ${source_var} := ${source_class}.new()\n\t\t# will be result in\n\t\tvar my_class := MyClass.new()\n\n\t# The full resource path from which the file was created.\n\t${source_resource_path}\n\t\t# Can be used to load the script in your test\n\t\tvar my_script := load(${source_resource_path})\n\t\t# will be result in\n\t\tvar my_script := load(\"res://folder/my_class.gd\")\n\"\"\"\n\nconst SUPPORTED_TAGS_CS = \"\"\"\n\tC# Tags are replaced when the test-suite is created.\n\n\t// The namespace name of the test-suite\n\t${name_space}\n\t\tnamespace ${name_space}\n\n\t// The class name of the test-suite, formed from the source class.\n\t${suite_class_name}\n\t\t// is used to build the test suite class name\n\t\t[TestSuite]\n\t\tpublic class ${suite_class_name}\n\n\t// The class name formed from the source class.\n\t${source_class}\n\t\t// can be used to create the class e.g. for source 'MyClass'\n\t\tprivate string myTestClass = new ${source_class}();\n\t\t// will be result in\n\t\tprivate string myTestClass = new MyClass();\n\n\t// The class as variable name in camelCase, formed from the source class.\n\t${source_var}\n\t\t// Can be used to build the variable name e.g. for source 'MyClass'\n\t\tprivate object ${source_var} = new ${source_class}();\n\t\t// will be result in\n\t\tprivate object myClass = new MyClass();\n\n\t// The full resource path from which the file was created.\n\t${source_resource_path}\n\t\t// Can be used to load the script in your test\n\t\tprivate object myScript = GD.Load(${source_resource_path});\n\t\t// will be result in\n\t\tprivate object myScript = GD.Load(\"res://folder/MyClass.cs\");\n\"\"\"\n\nconst TAG_TEST_SUITE_CLASS = \"${suite_class_name}\"\nconst TAG_SOURCE_CLASS_NAME = \"${source_class}\"\nconst TAG_SOURCE_CLASS_VARNAME = \"${source_var}\"\nconst TAG_SOURCE_RESOURCE_PATH = \"${source_resource_path}\"\n\n\nstatic func default_GD_template() -> String:\n\treturn GdUnitTestSuiteDefaultTemplate.DEFAULT_TEMP_TS_GD.dedent().trim_prefix(\"\\n\")\n\n\nstatic func default_CS_template() -> String:\n\treturn GdUnitTestSuiteDefaultTemplate.DEFAULT_TEMP_TS_CS.dedent().trim_prefix(\"\\n\")\n\n\nstatic func build_template(source_path: String) -> String:\n\tvar clazz_name :String = GdObjects.to_pascal_case(GdObjects.extract_class_name(source_path).value_as_string())\n\tvar template: String = GdUnitSettings.get_setting(GdUnitSettings.TEMPLATE_TS_GD, default_GD_template())\n\n\treturn template\\\n\t\t.replace(TAG_TEST_SUITE_CLASS, clazz_name+\"Test\")\\\n\t\t.replace(TAG_SOURCE_RESOURCE_PATH, source_path)\\\n\t\t.replace(TAG_SOURCE_CLASS_NAME, clazz_name)\\\n\t\t.replace(TAG_SOURCE_CLASS_VARNAME, GdObjects.to_snake_case(clazz_name))\n\n\nstatic func default_template(template_id :int) -> String:\n\tif template_id != TEMPLATE_ID_GD and template_id != TEMPLATE_ID_CS:\n\t\tpush_error(\"Invalid template '%d' id! Cant load testsuite template\" % template_id)\n\t\treturn \"\"\n\tif template_id == TEMPLATE_ID_GD:\n\t\treturn default_GD_template()\n\treturn default_CS_template()\n\n\nstatic func load_template(template_id :int) -> String:\n\tif template_id != TEMPLATE_ID_GD and template_id != TEMPLATE_ID_CS:\n\t\tpush_error(\"Invalid template '%d' id! Cant load testsuite template\" % template_id)\n\t\treturn \"\"\n\tif template_id == TEMPLATE_ID_GD:\n\t\treturn GdUnitSettings.get_setting(GdUnitSettings.TEMPLATE_TS_GD, default_GD_template())\n\treturn GdUnitSettings.get_setting(GdUnitSettings.TEMPLATE_TS_CS, default_CS_template())\n\n\nstatic func save_template(template_id :int, template :String) -> void:\n\tif template_id != TEMPLATE_ID_GD and template_id != TEMPLATE_ID_CS:\n\t\tpush_error(\"Invalid template '%d' id! Cant load testsuite template\" % template_id)\n\t\treturn\n\tif template_id == TEMPLATE_ID_GD:\n\t\tGdUnitSettings.save_property(GdUnitSettings.TEMPLATE_TS_GD, template.dedent().trim_prefix(\"\\n\"))\n\telif template_id == TEMPLATE_ID_CS:\n\t\tGdUnitSettings.save_property(GdUnitSettings.TEMPLATE_TS_CS, template.dedent().trim_prefix(\"\\n\"))\n\n\nstatic func reset_to_default(template_id :int) -> void:\n\tif template_id != TEMPLATE_ID_GD and template_id != TEMPLATE_ID_CS:\n\t\tpush_error(\"Invalid template '%d' id! Cant load testsuite template\" % template_id)\n\t\treturn\n\tif template_id == TEMPLATE_ID_GD:\n\t\tGdUnitSettings.save_property(GdUnitSettings.TEMPLATE_TS_GD, default_GD_template())\n\telse:\n\t\tGdUnitSettings.save_property(GdUnitSettings.TEMPLATE_TS_CS, default_CS_template())\n\n\nstatic func load_tags(template_id :int) -> String:\n\tif template_id != TEMPLATE_ID_GD and template_id != TEMPLATE_ID_CS:\n\t\tpush_error(\"Invalid template '%d' id! Cant load testsuite template\" % template_id)\n\t\treturn \"Error checked loading tags\"\n\tif template_id == TEMPLATE_ID_GD:\n\t\treturn SUPPORTED_TAGS_GD\n\telse:\n\t\treturn SUPPORTED_TAGS_CS\n"
  },
  {
    "path": "addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteTemplate.gd.uid",
    "content": "uid://giyf8m3efeyk\n"
  },
  {
    "path": "addons/gdUnit4/src/core/thread/GdUnitThreadContext.gd",
    "content": "class_name GdUnitThreadContext\nextends RefCounted\n\nvar _thread :Thread\nvar _thread_name :String\nvar _thread_id :int\nvar _signal_collector :GdUnitSignalCollector\nvar _execution_context :GdUnitExecutionContext\nvar _asserts := []\n\n\nfunc _init(thread :Thread = null) -> void:\n\tif thread != null:\n\t\t_thread = thread\n\t\t_thread_name = thread.get_meta(\"name\")\n\t\t_thread_id = thread.get_id() as int\n\telse:\n\t\t_thread_name = \"main\"\n\t\t_thread_id = OS.get_main_thread_id()\n\t_signal_collector = GdUnitSignalCollector.new()\n\n\nfunc dispose() -> void:\n\tclear_assert()\n\tif is_instance_valid(_signal_collector):\n\t\t_signal_collector.clear()\n\t_signal_collector = null\n\t_execution_context = null\n\t_thread = null\n\n\nfunc clear_assert() -> void:\n\t_asserts.clear()\n\n\nfunc set_assert(value :GdUnitAssert) -> void:\n\tif value != null:\n\t\t_asserts.append(value)\n\n\nfunc get_assert() -> GdUnitAssert:\n\treturn null if _asserts.is_empty() else _asserts[-1]\n\n\nfunc set_execution_context(context :GdUnitExecutionContext) -> void:\n\t_execution_context = context\n\n\nfunc get_execution_context() -> GdUnitExecutionContext:\n\treturn _execution_context\n\n\nfunc get_execution_context_id() -> int:\n\treturn _execution_context.get_instance_id()\n\n\nfunc get_signal_collector() -> GdUnitSignalCollector:\n\treturn _signal_collector\n\n\nfunc thread_id() -> int:\n\treturn _thread_id\n\n\nfunc _to_string() -> String:\n\treturn \"ThreadContext <%s>: %s \" % [_thread_name, _thread_id]\n"
  },
  {
    "path": "addons/gdUnit4/src/core/thread/GdUnitThreadContext.gd.uid",
    "content": "uid://bi0dyv5t7f3t\n"
  },
  {
    "path": "addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd",
    "content": "## A manager to run new thread and crate a ThreadContext shared over the actual test run\nclass_name GdUnitThreadManager\nextends Object\n\n## { <thread_id> = <GdUnitThreadContext> }\nvar _thread_context_by_id := {}\n## holds the current thread id\nvar _current_thread_id :int = -1\n\nfunc _init() -> void:\n\t# add initail the main thread\n\t_current_thread_id = OS.get_thread_caller_id()\n\t_thread_context_by_id[OS.get_main_thread_id()] = GdUnitThreadContext.new()\n\n\nstatic func instance() -> GdUnitThreadManager:\n\treturn GdUnitSingleton.instance(\"GdUnitThreadManager\", func() -> GdUnitThreadManager: return GdUnitThreadManager.new())\n\n\n## Runs a new thread by given name and Callable.[br]\n## A new GdUnitThreadContext is created, which is used for the actual test execution.[br]\n## We need this custom implementation while this bug is not solved\n## Godot issue https://github.com/godotengine/godot/issues/79637\nstatic func run(name :String, cb :Callable) -> Variant:\n\treturn await instance()._run(name, cb)\n\n\n## Returns the current valid thread context\nstatic func get_current_context() -> GdUnitThreadContext:\n\treturn instance()._get_current_context()\n\n\nfunc _run(name :String, cb :Callable) -> Variant:\n\t# we do this hack because of `OS.get_thread_caller_id()` not returns the current id\n\t# when await process_frame is called inside the fread\n\tvar save_current_thread_id := _current_thread_id\n\tvar thread := Thread.new()\n\tthread.set_meta(\"name\", name)\n\t@warning_ignore(\"return_value_discarded\")\n\tthread.start(cb)\n\t_current_thread_id = thread.get_id() as int\n\t_register_thread(thread, _current_thread_id)\n\tvar result :Variant = await thread.wait_to_finish()\n\t_unregister_thread(_current_thread_id)\n\t# restore original thread id\n\t_current_thread_id = save_current_thread_id\n\treturn result\n\n\nfunc _register_thread(thread :Thread, thread_id :int) -> void:\n\tvar context := GdUnitThreadContext.new(thread)\n\t_thread_context_by_id[thread_id] = context\n\n\nfunc _unregister_thread(thread_id :int) -> void:\n\tvar context: GdUnitThreadContext = _thread_context_by_id.get(thread_id)\n\tif context:\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\t_thread_context_by_id.erase(thread_id)\n\t\tcontext.dispose()\n\n\nfunc _get_current_context() -> GdUnitThreadContext:\n\treturn _thread_context_by_id.get(_current_thread_id)\n"
  },
  {
    "path": "addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd.uid",
    "content": "uid://cf3ym201sqvdk\n"
  },
  {
    "path": "addons/gdUnit4/src/doubler/CallableDoubler.gd",
    "content": "## The helper class to allow to double Callable\n## Is just a wrapper to the original callable with the same function signature.\n##\n## Due to interface conflicts between 'Callable' and 'Object',\n## it is not possible to stub the 'call' and 'call_deferred' methods.\n##\n## The Callable interface and the Object class have overlapping method signatures,\n## which causes conflicts when attempting to stub these methods.\n## As a result, you cannot create stubs for 'call' and 'call_deferred' methods.\n\nclass_name CallableDoubler\n\n\nconst doubler_script :Script =  preload(\"res://addons/gdUnit4/src/doubler/CallableDoubler.gd\")\n\nvar _cb: Callable\n\n\nfunc _init(cb: Callable) -> void:\n\tassert(cb!=null, \"Invalid argument <cb> must not be null\")\n\t_cb = cb\n\n## --- helpers -----------------------------------------------------------------------------------------------------------------------------\nstatic func map_func_name(method_info: Dictionary) -> String:\n\treturn method_info[\"name\"]\n\n\n## We do not want to double all functions based on Object for this class\n## Is used on SpyBuilder to excluding functions to be doubled for Callable\nstatic func excluded_functions() -> PackedStringArray:\n\treturn ClassDB.class_get_method_list(\"Object\")\\\n\t\t.map(CallableDoubler.map_func_name)\\\n\t\t.filter(func (name: String) -> bool:\n\t\t\treturn !CallableDoubler.callable_functions().has(name))\n\n\nstatic func non_callable_functions(name: String) -> bool:\n\treturn ![\n\t\t# we allow \"_init\", is need to construct it,\n\t\t\"excluded_functions\",\n\t\t\"non_callable_functions\",\n\t\t\"callable_functions\",\n\t\t\"map_func_name\"\n\t\t].has(name)\n\n\n## Returns the list of supported Callable functions\nstatic func callable_functions() -> PackedStringArray:\n\tvar supported_functions :Array = doubler_script.get_script_method_list()\\\n\t\t.map(CallableDoubler.map_func_name)\\\n\t\t.filter(CallableDoubler.non_callable_functions)\n\t# We manually add these functions that we cannot/may not overwrite in this class\n\tsupported_functions.append_array([\"call_deferred\", \"callv\"])\n\treturn supported_functions\n\n\n## -----------------------------------------------------------------------------------------------------------------------------------------\n## Callable functions stubing\n## -----------------------------------------------------------------------------------------------------------------------------------------\n\n@warning_ignore(\"untyped_declaration\")\nfunc bind(arg0=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ1=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ2=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ3=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ4=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ5=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ6=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ7=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ8=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ9=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE) -> Callable:\n\t# save\n\tvar bind_values: Array = GdArrayTools.filter_value([arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9], GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE)\n\t_cb = _cb.bindv(bind_values)\n\treturn _cb\n\n\nfunc bindv(caller_args: Array) -> Callable:\n\t_cb = _cb.bindv(caller_args)\n\treturn _cb\n\n\n@warning_ignore(\"untyped_declaration\", \"native_method_override\", \"unused_parameter\")\nfunc call(arg0=null,\n\targ1=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ2=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ3=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ4=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ5=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ6=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ7=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ8=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ9=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE) -> Variant:\n\n\t# This is a placeholder function signanture without any functionallity!\n\t# It is used by the function doubler to double function signature of Callable:call()\n\t# The doubled function calls direct _cb.callv(<arguments>) see GdUnitSpyFunctionDoubler:TEMPLATE_CALLABLE_CALL template\n\tassert(false)\n\treturn null\n\n\n# Is not supported, see class description\n#func call_deferred(a) -> void:\n#\tpass\n\n\n# Is not supported, see class description\n#func callv(a) -> void:\n#\tpass\n\n\n\nfunc get_bound_arguments() -> Array:\n\treturn _cb.get_bound_arguments()\n\n\nfunc get_bound_arguments_count() -> int:\n\treturn _cb.get_bound_arguments_count()\n\n\nfunc get_method() -> StringName:\n\treturn _cb.get_method()\n\n\nfunc get_object() -> Object:\n\treturn _cb.get_object()\n\n\nfunc get_object_id() -> int:\n\treturn _cb.get_object_id()\n\n\nfunc hash() -> int:\n\treturn _cb.hash()\n\n\nfunc is_custom() -> bool:\n\treturn _cb.is_custom()\n\n\nfunc is_null() -> bool:\n\treturn _cb.is_null()\n\n\nfunc is_standard() -> bool:\n\treturn _cb.is_standard()\n\n\nfunc is_valid() -> bool:\n\treturn _cb.is_valid()\n\n\n@warning_ignore(\"untyped_declaration\")\nfunc rpc(arg0=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ1=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ2=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ3=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ4=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ5=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ6=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ7=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ8=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ9=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE) -> void:\n\n\tvar args: Array = GdArrayTools.filter_value([arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9], GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE)\n\tmatch args.size():\n\t\t0: _cb.rpc(0)\n\t\t1: _cb.rpc(args[0])\n\t\t2: _cb.rpc(args[0], args[1])\n\t\t3: _cb.rpc(args[0], args[1], args[2])\n\t\t4: _cb.rpc(args[0], args[1], args[2], args[3])\n\t\t5: _cb.rpc(args[0], args[1], args[2], args[3], args[4])\n\t\t6: _cb.rpc(args[0], args[1], args[2], args[3], args[4], args[5])\n\t\t7: _cb.rpc(args[0], args[1], args[2], args[3], args[4], args[5], args[6])\n\t\t8: _cb.rpc(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7])\n\t\t9: _cb.rpc(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8])\n\t\t10: _cb.rpc(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9])\n\n\n@warning_ignore(\"untyped_declaration\")\nfunc rpc_id(peer_id: int,\n\targ0=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ1=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ2=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ3=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ4=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ5=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ6=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ7=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ8=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE,\n\targ9=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE) -> void:\n\n\tvar args: Array = GdArrayTools.filter_value([arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9], GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE)\n\tmatch args.size():\n\t\t0: _cb.rpc_id(peer_id)\n\t\t1: _cb.rpc_id(peer_id, args[0])\n\t\t2: _cb.rpc_id(peer_id, args[0], args[1])\n\t\t3: _cb.rpc_id(peer_id, args[0], args[1], args[2])\n\t\t4: _cb.rpc_id(peer_id, args[0], args[1], args[2], args[3])\n\t\t5: _cb.rpc_id(peer_id, args[0], args[1], args[2], args[3], args[4])\n\t\t6: _cb.rpc_id(peer_id, args[0], args[1], args[2], args[3], args[4], args[5])\n\t\t7: _cb.rpc_id(peer_id, args[0], args[1], args[2], args[3], args[4], args[5], args[6])\n\t\t8: _cb.rpc_id(peer_id, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7])\n\t\t9: _cb.rpc_id(peer_id, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8])\n\t\t10: _cb.rpc_id(peer_id, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9])\n\n\nfunc unbind(argcount: int) -> Callable:\n\t_cb = _cb.unbind(argcount)\n\treturn _cb\n"
  },
  {
    "path": "addons/gdUnit4/src/doubler/CallableDoubler.gd.uid",
    "content": "uid://weolm6u5dpw0\n"
  },
  {
    "path": "addons/gdUnit4/src/extractors/GdUnitFuncValueExtractor.gd",
    "content": "# This class defines a value extractor by given function name and args\nclass_name GdUnitFuncValueExtractor\nextends GdUnitValueExtractor\n\nvar _func_names :PackedStringArray\nvar _args :Array\n\nfunc _init(func_name :String, p_args :Array) -> void:\n\t_func_names = func_name.split(\".\")\n\t_args = p_args\n\n\nfunc func_names() -> PackedStringArray:\n\treturn _func_names\n\n\nfunc args() -> Array:\n\treturn _args\n\n\n# Extracts a value by given `func_name` and `args`,\n# Allows to use a chained list of functions setarated ba a dot.\n#  e.g. \"func_a.func_b.name\"\n#  do calls instance.func_a().func_b().name() and returns finally the name\n# If a function returns an array, all elements will by collected in a array\n#  e.g. \"get_children.get_name\" checked a node\n#  do calls node.get_children() for all childs get_name() and returns all names in an array\n#\n# if the value not a Object or not accesible be `func_name` the value is converted to `\"n.a.\"`\n# expecing null values\nfunc extract_value(value: Variant) -> Variant:\n\tif value == null:\n\t\treturn null\n\tfor func_name in func_names():\n\t\tif GdArrayTools.is_array_type(value):\n\t\t\tvar values := Array()\n\t\t\t@warning_ignore(\"unsafe_cast\")\n\t\t\tfor element: Variant in (value as Array):\n\t\t\t\tvalues.append(_call_func(element, func_name))\n\t\t\tvalue = values\n\t\telse:\n\t\t\tvalue = _call_func(value, func_name)\n\t\tvar type := typeof(value)\n\t\tif type == TYPE_STRING_NAME:\n\t\t\treturn str(value)\n\t\tif type == TYPE_STRING and value == \"n.a.\":\n\t\t\treturn value\n\treturn value\n\n\nfunc _call_func(value :Variant, func_name :String) -> Variant:\n\t# for array types we need to call explicit by function name, using funcref is only supported for Objects\n\t# TODO extend to all array functions\n\tif GdArrayTools.is_array_type(value) and func_name == \"empty\":\n\t\t@warning_ignore(\"unsafe_cast\")\n\t\treturn (value as Array).is_empty()\n\n\tif is_instance_valid(value):\n\t\t# extract from function\n\t\tvar obj_value: Object = value\n\t\tif obj_value.has_method(func_name):\n\t\t\tvar extract := Callable(obj_value, func_name)\n\t\t\tif extract.is_valid():\n\t\t\t\treturn obj_value.call(func_name) if args().is_empty() else obj_value.callv(func_name, args())\n\t\telse:\n\t\t\t# if no function exists than try to extract form parmeters\n\t\t\tvar parameter: Variant = obj_value.get(func_name)\n\t\t\tif parameter != null:\n\t\t\t\treturn parameter\n\t# nothing found than return 'n.a.'\n\tif GdUnitSettings.is_verbose_assert_warnings():\n\t\tpush_warning(\"Extracting value from element '%s' by func '%s' failed! Converting to \\\"n.a.\\\"\" % [value, func_name])\n\treturn \"n.a.\"\n"
  },
  {
    "path": "addons/gdUnit4/src/extractors/GdUnitFuncValueExtractor.gd.uid",
    "content": "uid://c3p5akrp4kd72\n"
  },
  {
    "path": "addons/gdUnit4/src/fuzzers/FloatFuzzer.gd",
    "content": "class_name FloatFuzzer\nextends Fuzzer\n\nvar _from: float = 0\nvar _to: float = 0\n\nfunc _init(from: float, to: float) -> void:\n\tassert(from <= to, \"Invalid range!\")\n\t_from = from\n\t_to = to\n\nfunc next_value() -> float:\n\treturn randf_range(_from, _to)\n"
  },
  {
    "path": "addons/gdUnit4/src/fuzzers/FloatFuzzer.gd.uid",
    "content": "uid://ctoyhkbgfis17\n"
  },
  {
    "path": "addons/gdUnit4/src/fuzzers/Fuzzer.gd",
    "content": "# Base interface for fuzz testing\n# https://en.wikipedia.org/wiki/Fuzzing\nclass_name Fuzzer\nextends RefCounted\n# To run a test with a specific fuzzer you have to add defailt argument checked your test case\n# all arguments are optional []\n# syntax:\n# \tfunc test_foo([fuzzer = <Fuzzer>], [fuzzer_iterations=<amount>], [fuzzer_seed=<number>])\n# example:\n#   # runs the test 'test_foo' 10 times with a random int value generated by the IntFuzzer\n# \tfunc test_foo(fuzzer = Fuzzers.randomInt(), fuzzer_iterations=10)\n#\n#   # runs the test 'test_foo2' 1000 times as default with a random seed='101010101'\n# \tfunc test_foo2(fuzzer = Fuzzers.randomInt(), fuzzer_seed=101010101)\n\nconst ITERATION_DEFAULT_COUNT = 1000\nconst ARGUMENT_FUZZER_INSTANCE := \"fuzzer\"\nconst ARGUMENT_ITERATIONS := \"fuzzer_iterations\"\nconst ARGUMENT_SEED := \"fuzzer_seed\"\n\nvar _iteration_index :int = 0\nvar _iteration_limit :int = ITERATION_DEFAULT_COUNT\n\n\n# generates the next fuzz value\n# needs to be implement\nfunc next_value() -> Variant:\n\tpush_error(\"Invalid vall. Fuzzer not implemented 'next_value()'\")\n\treturn null\n\n\n# returns the current iteration index\nfunc iteration_index() -> int:\n\treturn _iteration_index\n\n\n# returns the amount of iterations where the fuzzer will be run\nfunc iteration_limit() -> int:\n\treturn _iteration_limit\n"
  },
  {
    "path": "addons/gdUnit4/src/fuzzers/Fuzzer.gd.uid",
    "content": "uid://dgkrmdevxbwys\n"
  },
  {
    "path": "addons/gdUnit4/src/fuzzers/IntFuzzer.gd",
    "content": "class_name IntFuzzer\nextends Fuzzer\n\nenum {\n\tNORMAL,\n\tEVEN,\n\tODD\n}\n\nvar _from :int = 0\nvar _to : int = 0\nvar _mode : int = NORMAL\n\n\nfunc _init(from: int, to: int, mode :int = NORMAL) -> void:\n\tassert(from <= to, \"Invalid range!\")\n\t_from = from\n\t_to = to\n\t_mode = mode\n\n\nfunc next_value() -> int:\n\tvar value := randi_range(_from, _to)\n\tmatch _mode:\n\t\tNORMAL:\n\t\t\treturn value\n\t\tEVEN:\n\t\t\treturn int((value / 2.0) * 2)\n\t\tODD:\n\t\t\treturn int((value / 2.0) * 2 + 1)\n\t\t_:\n\t\t\treturn value\n"
  },
  {
    "path": "addons/gdUnit4/src/fuzzers/IntFuzzer.gd.uid",
    "content": "uid://fctgquwg0lsk\n"
  },
  {
    "path": "addons/gdUnit4/src/fuzzers/StringFuzzer.gd",
    "content": "class_name StringFuzzer\nextends Fuzzer\n\n\nconst DEFAULT_CHARSET = \"a-zA-Z0-9+-_\"\n\nvar _min_length :int\nvar _max_length :int\nvar _charset :PackedByteArray\n\n\nfunc _init(min_length :int, max_length :int, pattern :String = DEFAULT_CHARSET) -> void:\n\tassert(min_length>0 and min_length < max_length)\n\tassert(not null or not pattern.is_empty())\n\t_min_length = min_length\n\t_max_length = max_length\n\t_charset = StringFuzzer.extract_charset(pattern)\n\n\nstatic func extract_charset(pattern :String) -> PackedByteArray:\n\tvar reg := RegEx.new()\n\tif reg.compile(pattern) != OK:\n\t\tpush_error(\"Invalid pattern to generate Strings! Use e.g  'a-zA-Z0-9+-_'\")\n\t\treturn PackedByteArray()\n\n\tvar charset := Array()\n\tvar char_before := -1\n\tvar index := 0\n\twhile index < pattern.length():\n\t\tvar char_current := pattern.unicode_at(index)\n\t\t# - range token at first or last pos?\n\t\tif char_current == 45 and (index == 0 or index == pattern.length()-1):\n\t\t\tcharset.append(char_current)\n\t\t\tindex += 1\n\t\t\tcontinue\n\t\tindex += 1\n\t\t# range starts\n\t\tif char_current == 45 and char_before != -1:\n\t\t\tvar char_next := pattern.unicode_at(index)\n\t\t\tvar characters := build_chars(char_before, char_next)\n\t\t\tfor character in characters:\n\t\t\t\tcharset.append(character)\n\t\t\tchar_before = -1\n\t\t\tindex += 1\n\t\t\tcontinue\n\t\tchar_before = char_current\n\t\tcharset.append(char_current)\n\treturn PackedByteArray(charset)\n\n\nstatic func build_chars(from :int, to :int) -> Array[int]:\n\tvar characters :Array[int] = []\n\tfor character in range(from+1, to+1):\n\t\tcharacters.append(character)\n\treturn characters\n\n\nfunc next_value() -> String:\n\tvar value := PackedByteArray()\n\tvar max_char := len(_charset)\n\tvar length :int = max(_min_length, randi() % _max_length)\n\tfor i in length:\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tvalue.append(_charset[randi() % max_char])\n\treturn value.get_string_from_utf8()\n"
  },
  {
    "path": "addons/gdUnit4/src/fuzzers/StringFuzzer.gd.uid",
    "content": "uid://qyx1hrh1hiiu\n"
  },
  {
    "path": "addons/gdUnit4/src/fuzzers/Vector2Fuzzer.gd",
    "content": "class_name Vector2Fuzzer\nextends Fuzzer\n\n\nvar _from :Vector2\nvar _to : Vector2\n\n\nfunc _init(from: Vector2, to: Vector2) -> void:\n\tassert(from <= to, \"Invalid range!\")\n\t_from = from\n\t_to = to\n\n\nfunc next_value() -> Vector2:\n\tvar x := randf_range(_from.x, _to.x)\n\tvar y := randf_range(_from.y, _to.y)\n\treturn Vector2(x, y)\n"
  },
  {
    "path": "addons/gdUnit4/src/fuzzers/Vector2Fuzzer.gd.uid",
    "content": "uid://hljjhsipf450\n"
  },
  {
    "path": "addons/gdUnit4/src/fuzzers/Vector3Fuzzer.gd",
    "content": "class_name Vector3Fuzzer\nextends Fuzzer\n\n\nvar _from :Vector3\nvar _to : Vector3\n\n\nfunc _init(from: Vector3, to: Vector3) -> void:\n\tassert(from <= to, \"Invalid range!\")\n\t_from = from\n\t_to = to\n\n\nfunc next_value() -> Vector3:\n\tvar x := randf_range(_from.x, _to.x)\n\tvar y := randf_range(_from.y, _to.y)\n\tvar z := randf_range(_from.z, _to.z)\n\treturn Vector3(x, y, z)\n"
  },
  {
    "path": "addons/gdUnit4/src/fuzzers/Vector3Fuzzer.gd.uid",
    "content": "uid://cjkwc6t3ah3vw\n"
  },
  {
    "path": "addons/gdUnit4/src/matchers/AnyArgumentMatcher.gd",
    "content": "class_name AnyArgumentMatcher\nextends GdUnitArgumentMatcher\n\n\n@warning_ignore(\"unused_parameter\")\nfunc is_match(value :Variant) -> bool:\n\treturn true\n\n\nfunc _to_string() -> String:\n\treturn \"any()\"\n"
  },
  {
    "path": "addons/gdUnit4/src/matchers/AnyArgumentMatcher.gd.uid",
    "content": "uid://n88mcx2usmqg\n"
  },
  {
    "path": "addons/gdUnit4/src/matchers/AnyBuildInTypeArgumentMatcher.gd",
    "content": "class_name AnyBuildInTypeArgumentMatcher\nextends GdUnitArgumentMatcher\n\nvar _type : PackedInt32Array = []\n\n\nfunc _init(type :PackedInt32Array) -> void:\n\t_type = type\n\n\nfunc is_match(value :Variant) -> bool:\n\treturn _type.has(typeof(value))\n\n\nfunc _to_string() -> String:\n\tmatch _type[0]:\n\t\tTYPE_BOOL: return \"any_bool()\"\n\t\tTYPE_STRING, TYPE_STRING_NAME: return \"any_string()\"\n\t\tTYPE_INT: return \"any_int()\"\n\t\tTYPE_FLOAT: return \"any_float()\"\n\t\tTYPE_COLOR: return \"any_color()\"\n\t\tTYPE_VECTOR2: return \"any_vector2()\" if _type.size() == 1 else \"any_vector()\"\n\t\tTYPE_VECTOR2I: return \"any_vector2i()\"\n\t\tTYPE_VECTOR3: return \"any_vector3()\"\n\t\tTYPE_VECTOR3I: return \"any_vector3i()\"\n\t\tTYPE_VECTOR4: return \"any_vector4()\"\n\t\tTYPE_VECTOR4I: return \"any_vector4i()\"\n\t\tTYPE_RECT2: return \"any_rect2()\"\n\t\tTYPE_RECT2I: return \"any_rect2i()\"\n\t\tTYPE_PLANE: return \"any_plane()\"\n\t\tTYPE_QUATERNION: return \"any_quat()\"\n\t\tTYPE_AABB: return \"any_aabb()\"\n\t\tTYPE_BASIS: return \"any_basis()\"\n\t\tTYPE_TRANSFORM2D: return \"any_transform_2d()\"\n\t\tTYPE_TRANSFORM3D: return \"any_transform_3d()\"\n\t\tTYPE_NODE_PATH: return \"any_node_path()\"\n\t\tTYPE_RID: return \"any_rid()\"\n\t\tTYPE_OBJECT: return \"any_object()\"\n\t\tTYPE_DICTIONARY: return \"any_dictionary()\"\n\t\tTYPE_ARRAY: return \"any_array()\"\n\t\tTYPE_PACKED_BYTE_ARRAY: return \"any_packed_byte_array()\"\n\t\tTYPE_PACKED_INT32_ARRAY: return \"any_packed_int32_array()\"\n\t\tTYPE_PACKED_INT64_ARRAY: return \"any_packed_int64_array()\"\n\t\tTYPE_PACKED_FLOAT32_ARRAY: return \"any_packed_float32_array()\"\n\t\tTYPE_PACKED_FLOAT64_ARRAY: return \"any_packed_float64_array()\"\n\t\tTYPE_PACKED_STRING_ARRAY: return \"any_packed_string_array()\"\n\t\tTYPE_PACKED_VECTOR2_ARRAY: return \"any_packed_vector2_array()\"\n\t\tTYPE_PACKED_VECTOR3_ARRAY: return \"any_packed_vector3_array()\"\n\t\tTYPE_PACKED_COLOR_ARRAY: return \"any_packed_color_array()\"\n\t\t_: return \"any()\"\n"
  },
  {
    "path": "addons/gdUnit4/src/matchers/AnyBuildInTypeArgumentMatcher.gd.uid",
    "content": "uid://b77l245lpmuio\n"
  },
  {
    "path": "addons/gdUnit4/src/matchers/AnyClazzArgumentMatcher.gd",
    "content": "class_name AnyClazzArgumentMatcher\nextends GdUnitArgumentMatcher\n\nvar _clazz :Object\n\n\nfunc _init(clazz :Object) -> void:\n\t_clazz = clazz\n\n\nfunc is_match(value :Variant) -> bool:\n\tif typeof(value) != TYPE_OBJECT:\n\t\treturn false\n\tif is_instance_valid(value) and GdObjects.is_script(_clazz):\n\t\t@warning_ignore(\"unsafe_cast\")\n\t\treturn (value as Object).get_script() == _clazz\n\treturn is_instance_of(value, _clazz)\n\n\nfunc _to_string() -> String:\n\tif (_clazz as Object).is_class(\"GDScriptNativeClass\"):\n\t\t@warning_ignore(\"unsafe_method_access\")\n\t\tvar instance :Object = _clazz.new()\n\t\tvar clazz_name := instance.get_class()\n\t\tif not instance is RefCounted:\n\t\t\tinstance.free()\n\t\treturn \"any_class(<\"+clazz_name+\">)\";\n\tif _clazz is GDScript:\n\t\tvar result := GdObjects.extract_class_name(_clazz)\n\t\tif result.is_success():\n\t\t\treturn \"any_class(<\"+ result.value() + \">)\"\n\treturn \"any_class()\"\n"
  },
  {
    "path": "addons/gdUnit4/src/matchers/AnyClazzArgumentMatcher.gd.uid",
    "content": "uid://c6mprgcv7q27g\n"
  },
  {
    "path": "addons/gdUnit4/src/matchers/ChainedArgumentMatcher.gd",
    "content": "class_name ChainedArgumentMatcher\nextends GdUnitArgumentMatcher\n\nvar _matchers :Array\n\n\nfunc _init(matchers :Array) -> void:\n\t_matchers = matchers\n\n\nfunc is_match(arguments :Variant) -> bool:\n\tvar arg_array: Array = arguments\n\tif arg_array == null or arg_array.size() != _matchers.size():\n\t\treturn false\n\n\tfor index in arg_array.size():\n\t\tvar arg: Variant = arg_array[index]\n\t\tvar matcher: GdUnitArgumentMatcher = _matchers[index]\n\n\t\tif not matcher.is_match(arg):\n\t\t\treturn false\n\treturn true\n"
  },
  {
    "path": "addons/gdUnit4/src/matchers/ChainedArgumentMatcher.gd.uid",
    "content": "uid://dbu8eri7i77ny\n"
  },
  {
    "path": "addons/gdUnit4/src/matchers/EqualsArgumentMatcher.gd",
    "content": "class_name EqualsArgumentMatcher\nextends GdUnitArgumentMatcher\n\nvar _current :Variant\nvar _auto_deep_check_mode :bool\n\n\nfunc _init(current :Variant, auto_deep_check_mode := false) -> void:\n\t_current = current\n\t_auto_deep_check_mode = auto_deep_check_mode\n\n\nfunc is_match(value :Variant) -> bool:\n\tvar case_sensitive_check := true\n\treturn GdObjects.equals(_current, value, case_sensitive_check, compare_mode(value))\n\n\nfunc compare_mode(value :Variant) -> GdObjects.COMPARE_MODE:\n\tif _auto_deep_check_mode and is_instance_valid(value):\n\t\t# we do deep check on all InputEvent's\n\t\treturn GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST if value is InputEvent else GdObjects.COMPARE_MODE.OBJECT_REFERENCE\n\treturn GdObjects.COMPARE_MODE.OBJECT_REFERENCE\n"
  },
  {
    "path": "addons/gdUnit4/src/matchers/EqualsArgumentMatcher.gd.uid",
    "content": "uid://cw4vf7v1hwxdx\n"
  },
  {
    "path": "addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd",
    "content": "## The base class of all argument matchers\nclass_name GdUnitArgumentMatcher\nextends RefCounted\n\n\n@warning_ignore(\"unused_parameter\")\nfunc is_match(value :Variant) -> bool:\n\treturn true\n"
  },
  {
    "path": "addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd.uid",
    "content": "uid://cqddpmj34osl\n"
  },
  {
    "path": "addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd",
    "content": "class_name GdUnitArgumentMatchers\nextends RefCounted\n\nconst TYPE_ANY = TYPE_MAX + 100\n\n\nstatic func to_matcher(arguments :Array[Variant], auto_deep_check_mode := false) -> ChainedArgumentMatcher:\n\tvar matchers :Array[Variant] = []\n\tfor arg :Variant in arguments:\n\t\t# argument is already a matcher\n\t\tif arg is GdUnitArgumentMatcher:\n\t\t\tmatchers.append(arg)\n\t\telse:\n\t\t\t# pass argument into equals matcher\n\t\t\tmatchers.append(EqualsArgumentMatcher.new(arg, auto_deep_check_mode))\n\treturn ChainedArgumentMatcher.new(matchers)\n\n\nstatic func any() -> GdUnitArgumentMatcher:\n\treturn  AnyArgumentMatcher.new()\n\n\nstatic func by_type(type :int) -> GdUnitArgumentMatcher:\n\treturn AnyBuildInTypeArgumentMatcher.new([type])\n\n\nstatic func by_types(types :PackedInt32Array) -> GdUnitArgumentMatcher:\n\treturn AnyBuildInTypeArgumentMatcher.new(types)\n\n\nstatic func any_class(clazz :Object) -> GdUnitArgumentMatcher:\n\treturn AnyClazzArgumentMatcher.new(clazz)\n"
  },
  {
    "path": "addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd.uid",
    "content": "uid://oioflrg30qji\n"
  },
  {
    "path": "addons/gdUnit4/src/mocking/GdUnitMock.gd",
    "content": "class_name GdUnitMock\nextends RefCounted\n\n## do call the real implementation\nconst CALL_REAL_FUNC = \"CALL_REAL_FUNC\"\n## do return a default value for primitive types or null\nconst RETURN_DEFAULTS = \"RETURN_DEFAULTS\"\n## do return a default value for primitive types and a fully mocked value for Object types\n## builds full deep mocked object\nconst RETURN_DEEP_STUB = \"RETURN_DEEP_STUB\"\n\nvar _value: Variant\n\n\nfunc _init(value: Variant) -> void:\n\t_value = value\n\n\n## Selects the mock to work on, used in combination with [method GdUnitTestSuite.do_return][br]\n## Example:\n## \t[codeblock]\n## \t\tdo_return(false).on(myMock).is_selected()\n## \t[/codeblock]\nfunc on(obj: Variant) -> Variant:\n\tif not GdUnitMock._is_mock_or_spy(obj, \"__do_return\"):\n\t\treturn obj\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn obj.__do_return(_value)\n\n\n## [color=yellow]`checked` is obsolete, use `on` instead [/color]\nfunc checked(obj :Object) -> Object:\n\tpush_warning(\"Using a deprecated function 'checked' use `on` instead\")\n\treturn on(obj)\n\n\nstatic func _is_mock_or_spy(obj: Variant, func_sig: String) -> bool:\n\tif obj is Object and not as_object(obj).has_method(func_sig):\n\t\tpush_error(\"Error: You try to use a non mock or spy!\")\n\t\treturn false\n\treturn true\n\n\nstatic func as_object(value: Variant) -> Object:\n\treturn value\n"
  },
  {
    "path": "addons/gdUnit4/src/mocking/GdUnitMock.gd.uid",
    "content": "uid://c0nw2t3o6cp17\n"
  },
  {
    "path": "addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd",
    "content": "class_name GdUnitMockBuilder\nextends GdUnitClassDoubler\n\nconst GdUnitTools := preload(\"res://addons/gdUnit4/src/core/GdUnitTools.gd\")\nconst MOCK_TEMPLATE :GDScript = preload(\"res://addons/gdUnit4/src/mocking/GdUnitMockImpl.gd\")\n\n\nstatic func is_push_errors() -> bool:\n\treturn GdUnitSettings.is_report_push_errors()\n\n\n@warning_ignore(\"unsafe_method_access\", \"unsafe_cast\")\nstatic func build(clazz :Variant, mock_mode :String, debug_write := false) -> Variant:\n\tvar push_errors := is_push_errors()\n\tif not is_mockable(clazz, push_errors):\n\t\treturn null\n\t# mocking a scene?\n\tif GdObjects.is_scene(clazz):\n\t\treturn mock_on_scene(clazz as PackedScene, debug_write)\n\telif typeof(clazz) == TYPE_STRING and clazz.ends_with(\".tscn\"):\n\t\treturn mock_on_scene(load(clazz as String) as PackedScene, debug_write)\n\t# mocking a script\n\tvar instance := create_instance(clazz)\n\tvar mock := mock_on_script(instance, clazz, [ \"get_script\"], debug_write)\n\tif not instance is RefCounted:\n\t\tinstance.free()\n\tif mock == null:\n\t\treturn null\n\tvar mock_instance: Variant = mock.new()\n\tmock_instance.__set_script(mock)\n\tmock_instance.__set_singleton()\n\tmock_instance.__set_mode(mock_mode)\n\treturn register_auto_free(mock_instance)\n\n\n@warning_ignore(\"unsafe_method_access\", \"unsafe_cast\")\nstatic func create_instance(clazz: Variant) -> Object:\n\tif typeof(clazz) == TYPE_OBJECT and  (clazz as Object).is_class(\"GDScriptNativeClass\"):\n\t\treturn clazz.new()\n\telif (clazz is GDScript) || (typeof(clazz) == TYPE_STRING and clazz.ends_with(\".gd\")):\n\t\tvar script: GDScript = null\n\t\tif clazz is GDScript:\n\t\t\tscript = clazz\n\t\telse:\n\t\t\tscript = load(clazz as String)\n\n\t\tvar args := GdObjects.build_function_default_arguments(script, \"_init\")\n\t\treturn script.callv(\"new\", args)\n\telif typeof(clazz) == TYPE_STRING and ClassDB.can_instantiate(clazz as String):\n\t\treturn ClassDB.instantiate(clazz as String)\n\tpush_error(\"Can't create a mock validation instance from class: `%s`\" % clazz)\n\treturn null\n\n\n@warning_ignore(\"unsafe_method_access\")\nstatic func mock_on_scene(scene :PackedScene, debug_write :bool) -> Variant:\n\tvar push_errors := is_push_errors()\n\tif not scene.can_instantiate():\n\t\tif push_errors:\n\t\t\tpush_error(\"Can't instanciate scene '%s'\" % scene.resource_path)\n\t\treturn null\n\tvar scene_instance := scene.instantiate()\n\t# we can only mock checked a scene with attached script\n\tif scene_instance.get_script() == null:\n\t\tif push_errors:\n\t\t\tpush_error(\"Can't create a mockable instance for a scene without script '%s'\" % scene.resource_path)\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tGdUnitTools.free_instance(scene_instance)\n\t\treturn null\n\n\tvar script_path :String = scene_instance.get_script().get_path()\n\tvar mock := mock_on_script(scene_instance, script_path, GdUnitClassDoubler.EXLCUDE_SCENE_FUNCTIONS, debug_write)\n\tif mock == null:\n\t\treturn null\n\tscene_instance.set_script(mock)\n\tscene_instance.__set_singleton()\n\tscene_instance.__set_mode(GdUnitMock.CALL_REAL_FUNC)\n\treturn register_auto_free(scene_instance)\n\n\nstatic func get_class_info(clazz :Variant) -> Dictionary:\n\tvar clazz_name :String = GdObjects.extract_class_name(clazz).value()\n\tvar clazz_path := GdObjects.extract_class_path(clazz)\n\treturn {\n\t\t\"class_name\" : clazz_name,\n\t\t\"class_path\" : clazz_path\n\t}\n\n\nstatic func mock_on_script(instance :Object, clazz :Variant, function_excludes :PackedStringArray, debug_write :bool) -> GDScript:\n\tvar push_errors := is_push_errors()\n\tvar function_doubler := GdUnitMockFunctionDoubler.new(push_errors)\n\tvar class_info := get_class_info(clazz)\n\tvar lines := load_template(MOCK_TEMPLATE.source_code, class_info, instance)\n\n\tvar clazz_name :String = class_info.get(\"class_name\")\n\tvar clazz_path :PackedStringArray = class_info.get(\"class_path\", [clazz_name])\n\tlines += double_functions(instance, clazz_name, clazz_path, function_doubler, function_excludes)\n\n\tvar mock := GDScript.new()\n\tmock.source_code = \"\\n\".join(lines)\n\tmock.resource_name =  \"Mock%s_%d.gd\" % [clazz_name, Time.get_ticks_msec()]\n\tmock.resource_path = \"%s/%s\"  % [GdUnitFileAccess.create_temp_dir(\"mock\"), mock.resource_name]\n\n\tif debug_write:\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tDirAccess.remove_absolute(mock.resource_path)\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tResourceSaver.save(mock, mock.resource_path)\n\tvar error := mock.reload(true)\n\tif error != OK:\n\t\tpush_error(\"Critical!!!, MockBuilder error, please contact the developer.\")\n\t\treturn null\n\treturn mock\n\n\nstatic func is_mockable(clazz :Variant, push_errors :bool=false) -> bool:\n\tvar clazz_type := typeof(clazz)\n\tif clazz_type != TYPE_OBJECT and clazz_type != TYPE_STRING:\n\t\tpush_error(\"Invalid clazz type is used\")\n\t\treturn false\n\t# is PackedScene\n\tif GdObjects.is_scene(clazz):\n\t\treturn true\n\tif GdObjects.is_native_class(clazz):\n\t\treturn true\n\t# verify class type\n\tif GdObjects.is_object(clazz):\n\t\tif GdObjects.is_instance(clazz):\n\t\t\tif push_errors:\n\t\t\t\tpush_error(\"It is not allowed to mock an instance '%s', use class name instead, Read 'Mocker' documentation for details\" % clazz)\n\t\t\treturn false\n\n\t\tif not GdObjects.can_be_instantiate(clazz):\n\t\t\tif push_errors:\n\t\t\t\tpush_error(\"Can't create a mockable instance for class '%s'\" % clazz)\n\t\t\treturn false\n\t\treturn true\n\t# verify by class name checked registered classes\n\tvar clazz_name: String = clazz\n\tif ClassDB.class_exists(clazz_name):\n\t\tif Engine.has_singleton(clazz_name):\n\t\t\tif push_errors:\n\t\t\t\tpush_error(\"Mocking a singelton class '%s' is not allowed!  Read 'Mocker' documentation for details\" % clazz_name)\n\t\t\treturn false\n\t\tif not ClassDB.can_instantiate(clazz_name):\n\t\t\tif push_errors:\n\t\t\t\tpush_error(\"Mocking class '%s' is not allowed it cannot be instantiated!\" % clazz_name)\n\t\t\treturn false\n\t\t# exclude classes where name starts with a underscore\n\t\tif clazz_name.find(\"_\") == 0:\n\t\t\tif push_errors:\n\t\t\t\tpush_error(\"Can't create a mockable instance for protected class '%s'\" % clazz_name)\n\t\t\treturn false\n\t\treturn true\n\t# at least try to load as a script\n\tvar clazz_path := clazz_name\n\tif not FileAccess.file_exists(clazz_path):\n\t\tif push_errors:\n\t\t\tpush_error(\"'%s' cannot be mocked for the specified resource path, the resource does not exist\" % clazz_name)\n\t\treturn false\n\t# finally verify is a script resource\n\tvar resource := load(clazz_path)\n\tif resource == null:\n\t\tif push_errors:\n\t\t\tpush_error(\"'%s' cannot be mocked the script cannot be loaded.\" % clazz_name)\n\t\t\treturn false\n\t# finally check is extending from script\n\treturn GdObjects.is_script(resource) or GdObjects.is_scene(resource)\n\n\nstatic func register_auto_free(obj :Variant) -> Variant:\n\treturn GdUnitThreadManager.get_current_context().get_execution_context().register_auto_free(obj)\n"
  },
  {
    "path": "addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd.uid",
    "content": "uid://djadvx8a4j3gj\n"
  },
  {
    "path": "addons/gdUnit4/src/mocking/GdUnitMockFunctionDoubler.gd",
    "content": "class_name GdUnitMockFunctionDoubler\nextends GdFunctionDoubler\n\n\nconst TEMPLATE_FUNC_WITH_RETURN_VALUE = \"\"\"\n\tvar args__: Array = [\"$(func_name)\", $(arguments)]\n\n\tif $(instance)__is_prepare_return_value():\n\t\t$(instance)__save_function_return_value(args__)\n\t\treturn ${default_return_value}\n\tif $(instance)__is_verify_interactions():\n\t\t$(instance)__verify_interactions(args__)\n\t\treturn ${default_return_value}\n\telse:\n\t\t$(instance)__save_function_interaction(args__)\n\n\tif $(instance)__do_call_real_func(\"$(func_name)\", args__):\n\t\treturn $(await)super($(arguments))\n\treturn $(instance)__get_mocked_return_value_or_default(args__, ${default_return_value})\n\n\"\"\"\n\n\nconst TEMPLATE_FUNC_WITH_RETURN_VOID = \"\"\"\n\tvar args__: Array = [\"$(func_name)\", $(arguments)]\n\n\tif $(instance)__is_prepare_return_value():\n\t\tif $(push_errors):\n\t\t\tpush_error(\\\"Mocking a void function '$(func_name)(<args>) -> void:' is not allowed.\\\")\n\t\treturn\n\tif $(instance)__is_verify_interactions():\n\t\t$(instance)__verify_interactions(args__)\n\t\treturn\n\telse:\n\t\t$(instance)__save_function_interaction(args__)\n\n\tif $(instance)__do_call_real_func(\"$(func_name)\"):\n\t\t$(await)super($(arguments))\n\n\"\"\"\n\n\nconst TEMPLATE_FUNC_VARARG_RETURN_VALUE = \"\"\"\n\tvar varargs__: Array = __filter_vargs([$(varargs)])\n\tvar args__: Array = [\"$(func_name)\", $(arguments)] + varargs__\n\n\tif $(instance)__is_prepare_return_value():\n\t\tif $(push_errors):\n\t\t\tpush_error(\\\"Mocking a void function '$(func_name)(<args>) -> void:' is not allowed.\\\")\n\t\t$(instance)__save_function_return_value(args__)\n\t\treturn ${default_return_value}\n\tif $(instance)__is_verify_interactions():\n\t\t$(instance)__verify_interactions(args__)\n\t\treturn ${default_return_value}\n\telse:\n\t\t$(instance)__save_function_interaction(args__)\n\n\tif $(instance)__do_call_real_func(\"$(func_name)\", args__):\n\t\tmatch varargs__.size():\n\t\t\t0: return $(await)super($(arguments))\n\t\t\t1: return $(await)super($(arguments), varargs__[0])\n\t\t\t2: return $(await)super($(arguments), varargs__[0], varargs__[1])\n\t\t\t3: return $(await)super($(arguments), varargs__[0], varargs__[1], varargs__[2])\n\t\t\t4: return $(await)super($(arguments), varargs__[0], varargs__[1], varargs__[2], varargs__[3])\n\t\t\t5: return $(await)super($(arguments), varargs__[0], varargs__[1], varargs__[2], varargs__[3], varargs__[4])\n\t\t\t6: return $(await)super($(arguments), varargs__[0], varargs__[1], varargs__[2], varargs__[3], varargs__[4], varargs__[5])\n\t\t\t7: return $(await)super($(arguments), varargs__[0], varargs__[1], varargs__[2], varargs__[3], varargs__[4], varargs__[5], varargs__[6])\n\t\t\t8: return $(await)super($(arguments), varargs__[0], varargs__[1], varargs__[2], varargs__[3], varargs__[4], varargs__[5], varargs__[6], varargs__[7])\n\t\t\t9: return $(await)super($(arguments), varargs__[0], varargs__[1], varargs__[2], varargs__[3], varargs__[4], varargs__[5], varargs__[6], varargs__[7], varargs__[8])\n\t\t\t10: return $(await)super($(arguments), varargs__[0], varargs__[1], varargs__[2], varargs__[3], varargs__[4], varargs__[5], varargs__[6], varargs__[7], varargs__[8], varargs__[9])\n\treturn __get_mocked_return_value_or_default(args__, ${default_return_value})\n\n\"\"\"\n\n\nfunc _init(push_errors :bool = false) -> void:\n\tsuper._init(push_errors)\n\n\nfunc get_template(fd: GdFunctionDescriptor, _is_callable: bool) -> String:\n\tif fd.is_vararg():\n\t\treturn TEMPLATE_FUNC_VARARG_RETURN_VALUE\n\tvar return_type :Variant = fd.return_type()\n\tif return_type is StringName:\n\t\treturn TEMPLATE_FUNC_WITH_RETURN_VALUE\n\treturn TEMPLATE_FUNC_WITH_RETURN_VOID if (return_type == TYPE_NIL or return_type == GdObjects.TYPE_VOID) else TEMPLATE_FUNC_WITH_RETURN_VALUE\n"
  },
  {
    "path": "addons/gdUnit4/src/mocking/GdUnitMockFunctionDoubler.gd.uid",
    "content": "uid://u60ttdv3kkao\n"
  },
  {
    "path": "addons/gdUnit4/src/mocking/GdUnitMockImpl.gd",
    "content": "\n################################################################################\n# internal mocking stuff\n################################################################################\nconst __INSTANCE_ID = \"${instance_id}\"\nconst __SOURCE_CLASS = \"${source_class}\"\n\nvar __mock_working_mode := GdUnitMock.RETURN_DEFAULTS\nvar __excluded_methods :PackedStringArray = []\nvar __do_return_value :Variant = null\nvar __prepare_return_value := false\n\n#{ <func_name> = {\n#\t\t<func_args> = <return_value>\n#\t}\n#}\nvar __mocked_return_values := Dictionary()\n\n\nstatic func __instance() -> Object:\n\treturn Engine.get_meta(__INSTANCE_ID)\n\n\nfunc _notification(what :int) -> void:\n\tif what == NOTIFICATION_PREDELETE:\n\t\tif Engine.has_meta(__INSTANCE_ID):\n\t\t\tEngine.remove_meta(__INSTANCE_ID)\n\n\nfunc __instance_id() -> String:\n\treturn __INSTANCE_ID\n\n\nfunc __set_singleton() -> void:\n\t# store self need to mock static functions\n\tEngine.set_meta(__INSTANCE_ID, self)\n\n\nfunc __release_double() -> void:\n\t# we need to release the self reference manually to prevent orphan nodes\n\tEngine.remove_meta(__INSTANCE_ID)\n\n\nfunc __is_prepare_return_value() -> bool:\n\treturn __prepare_return_value\n\n\nfunc __sort_by_argument_matcher(__left_args :Array, __right_args :Array) -> bool:\n\tfor __index in __left_args.size():\n\t\tvar __larg :Variant = __left_args[__index]\n\t\tif __larg is GdUnitArgumentMatcher:\n\t\t\treturn false\n\treturn true\n\n\n# we need to sort by matcher arguments so that they are all at the end of the list\nfunc __sort_dictionary(__unsorted_args :Dictionary) -> Dictionary:\n\t# only need to sort if contains more than one entry\n\tif __unsorted_args.size() <= 1:\n\t\treturn __unsorted_args\n\tvar __sorted_args := __unsorted_args.keys()\n\t__sorted_args.sort_custom(__sort_by_argument_matcher)\n\tvar __sorted_result := {}\n\tfor __index in __sorted_args.size():\n\t\tvar key :Variant = __sorted_args[__index]\n\t\t__sorted_result[key] = __unsorted_args[key]\n\treturn __sorted_result\n\n\nfunc __save_function_return_value(__fuction_args :Array) -> void:\n\tvar __func_name :String = __fuction_args[0]\n\tvar __func_args :Array = __fuction_args.slice(1)\n\tvar __mocked_return_value_by_args :Dictionary = __mocked_return_values.get(__func_name, {})\n\t__mocked_return_value_by_args[__func_args] = __do_return_value\n\t__mocked_return_values[__func_name] = __sort_dictionary(__mocked_return_value_by_args)\n\t__do_return_value = null\n\t__prepare_return_value = false\n\n\n@warning_ignore(\"unsafe_method_access\")\nfunc __is_mocked_args_match(__func_args :Array, __mocked_args :Array) -> bool:\n\tvar __is_matching := false\n\tfor __index in __mocked_args.size():\n\t\tvar __fuction_args :Variant = __mocked_args[__index]\n\t\tif __func_args.size() != __fuction_args.size():\n\t\t\tcontinue\n\t\t__is_matching = true\n\t\tfor __arg_index in __func_args.size():\n\t\t\tvar __func_arg :Variant = __func_args[__arg_index]\n\t\t\tvar __mock_arg :Variant = __fuction_args[__arg_index]\n\t\t\tif __mock_arg is GdUnitArgumentMatcher:\n\t\t\t\t__is_matching = __is_matching and __mock_arg.is_match(__func_arg)\n\t\t\telse:\n\t\t\t\t__is_matching = __is_matching and typeof(__func_arg) == typeof(__mock_arg) and __func_arg == __mock_arg\n\t\t\tif not __is_matching:\n\t\t\t\tbreak\n\t\tif __is_matching:\n\t\t\tbreak\n\treturn __is_matching\n\n\n@warning_ignore(\"unsafe_method_access\")\nfunc __get_mocked_return_value_or_default(__fuction_args :Array, __default_return_value :Variant) -> Variant:\n\tvar __func_name :String = __fuction_args[0]\n\tif not __mocked_return_values.has(__func_name):\n\t\treturn __default_return_value\n\tvar __func_args :Array = __fuction_args.slice(1)\n\tvar __mocked_args :Array = __mocked_return_values.get(__func_name).keys()\n\tfor __index in __mocked_args.size():\n\t\tvar __margs :Variant = __mocked_args[__index]\n\t\tif __is_mocked_args_match(__func_args, [__margs]):\n\t\t\treturn __mocked_return_values[__func_name][__margs]\n\treturn __default_return_value\n\n\nfunc __set_script(__script :GDScript) -> void:\n\tsuper.set_script(__script)\n\n\nfunc __set_mode(mock_working_mode :String) -> Object:\n\t__mock_working_mode = mock_working_mode\n\treturn self\n\n\n@warning_ignore(\"unsafe_method_access\")\nfunc __do_call_real_func(__func_name :String, __func_args := []) -> bool:\n\tvar __is_call_real_func := __mock_working_mode == GdUnitMock.CALL_REAL_FUNC  and not __excluded_methods.has(__func_name)\n\t# do not call real funcions for mocked functions\n\tif __is_call_real_func and __mocked_return_values.has(__func_name):\n\t\tvar __fuction_args :Array = __func_args.slice(1)\n\t\tvar __mocked_args :Array = __mocked_return_values.get(__func_name).keys()\n\t\treturn not __is_mocked_args_match(__fuction_args, __mocked_args)\n\treturn __is_call_real_func\n\n\nfunc __exclude_method_call(exluded_methods :PackedStringArray) -> void:\n\t__excluded_methods.append_array(exluded_methods)\n\n\nfunc __do_return(mock_do_return_value :Variant) -> Object:\n\t__do_return_value = mock_do_return_value\n\t__prepare_return_value = true\n\treturn self\n"
  },
  {
    "path": "addons/gdUnit4/src/mocking/GdUnitMockImpl.gd.uid",
    "content": "uid://daaxxuutbom8c\n"
  },
  {
    "path": "addons/gdUnit4/src/monitor/ErrorLogEntry.gd",
    "content": "extends RefCounted\nclass_name ErrorLogEntry\n\n\nenum TYPE {\n\tSCRIPT_ERROR,\n\tPUSH_ERROR,\n\tPUSH_WARNING\n}\n\n\nconst GdUnitTools := preload(\"res://addons/gdUnit4/src/core/GdUnitTools.gd\")\n\nconst PATTERN_SCRIPT_ERROR := \"USER SCRIPT ERROR:\"\nconst PATTERN_PUSH_ERROR := \"USER ERROR:\"\nconst PATTERN_PUSH_WARNING := \"USER WARNING:\"\n# With Godot 4.4 the pattern has changed\nconst PATTERN_4x4_SCRIPT_ERROR := \"SCRIPT ERROR:\"\nconst PATTERN_4x4_PUSH_ERROR := \"ERROR:\"\nconst PATTERN_4x4_PUSH_WARNING := \"WARNING:\"\n\nstatic var _regex_parse_error_line_number: RegEx\n\nvar _type: TYPE\nvar _line: int\nvar _message: String\nvar _details: String\n\n\nfunc _init(type: TYPE, line: int, message: String, details: String) -> void:\n\t_type = type\n\t_line = line\n\t_message = message\n\t_details = details\n\n\nstatic func is_godot4x4() -> bool:\n\treturn Engine.get_version_info().hex >= 0x40400\n\n\nstatic func extract_push_warning(records: PackedStringArray, index: int) -> ErrorLogEntry:\n\tvar pattern := PATTERN_4x4_PUSH_WARNING if is_godot4x4() else PATTERN_PUSH_WARNING\n\treturn _extract(records, index, TYPE.PUSH_WARNING, pattern)\n\n\nstatic func extract_push_error(records: PackedStringArray, index: int) -> ErrorLogEntry:\n\tvar pattern := PATTERN_4x4_PUSH_ERROR if is_godot4x4() else PATTERN_PUSH_ERROR\n\treturn _extract(records, index, TYPE.PUSH_ERROR, pattern)\n\n\nstatic func extract_error(records: PackedStringArray, index: int) -> ErrorLogEntry:\n\tvar pattern := PATTERN_4x4_SCRIPT_ERROR if is_godot4x4() else PATTERN_SCRIPT_ERROR\n\treturn _extract(records, index, TYPE.SCRIPT_ERROR, pattern)\n\n\nstatic func _extract(records: PackedStringArray, index: int, type: TYPE, pattern: String) -> ErrorLogEntry:\n\tvar message := records[index]\n\tif message.begins_with(pattern):\n\t\tvar error := message.replace(pattern, \"\").strip_edges()\n\t\tvar details := records[index+1].strip_edges()\n\t\tvar line := _parse_error_line_number(details)\n\t\treturn ErrorLogEntry.new(type, line, error, details)\n\treturn null\n\n\nstatic func _parse_error_line_number(record: String) -> int:\n\tif _regex_parse_error_line_number == null:\n\t\t_regex_parse_error_line_number = GdUnitTools.to_regex(\"at: .*res://.*:(\\\\d+)\")\n\tvar matches := _regex_parse_error_line_number.search(record)\n\tif matches != null:\n\t\treturn matches.get_string(1).to_int()\n\treturn -1\n"
  },
  {
    "path": "addons/gdUnit4/src/monitor/ErrorLogEntry.gd.uid",
    "content": "uid://di3i6gl654n8o\n"
  },
  {
    "path": "addons/gdUnit4/src/monitor/GdUnitMonitor.gd",
    "content": "# GdUnit Monitoring Base Class\nclass_name GdUnitMonitor\nextends RefCounted\n\nvar _id :String\n\n# constructs new Monitor with given id\nfunc _init(p_id :String) -> void:\n\t_id = p_id\n\n\n# Returns the id of the monitor to uniqe identify\nfunc id() -> String:\n\treturn _id\n\n\n# starts monitoring\nfunc start() -> void:\n\tpass\n\n\n# stops monitoring\nfunc stop() -> void:\n\tpass\n"
  },
  {
    "path": "addons/gdUnit4/src/monitor/GdUnitMonitor.gd.uid",
    "content": "uid://b1y8g77et6qdc\n"
  },
  {
    "path": "addons/gdUnit4/src/monitor/GdUnitOrphanNodesMonitor.gd",
    "content": "class_name GdUnitOrphanNodesMonitor\nextends GdUnitMonitor\n\nvar _initial_count := 0\nvar _orphan_count := 0\nvar _orphan_detection_enabled :bool\n\n\nfunc _init(name :String = \"\") -> void:\n\tsuper(\"OrphanNodesMonitor:\" + name)\n\t_orphan_detection_enabled = GdUnitSettings.is_verbose_orphans()\n\n\nfunc start() -> void:\n\t_initial_count = _orphans()\n\n\nfunc stop() -> void:\n\t_orphan_count = max(0, _orphans() - _initial_count)\n\n\nfunc _orphans() -> int:\n\treturn Performance.get_monitor(Performance.OBJECT_ORPHAN_NODE_COUNT) as int\n\n\nfunc orphan_nodes() -> int:\n\treturn _orphan_count if _orphan_detection_enabled else 0\n"
  },
  {
    "path": "addons/gdUnit4/src/monitor/GdUnitOrphanNodesMonitor.gd.uid",
    "content": "uid://bgbhjawtf0dme\n"
  },
  {
    "path": "addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd",
    "content": "class_name GodotGdErrorMonitor\nextends GdUnitMonitor\n\nvar _godot_log_file: String\nvar _eof: int\nvar _report_enabled := false\nvar _entries: Array[ErrorLogEntry] = []\n\n\nfunc _init() -> void:\n\tsuper(\"GodotGdErrorMonitor\")\n\t_godot_log_file = GdUnitSettings.get_log_path()\n\t_report_enabled = _is_reporting_enabled()\n\n\nfunc start() -> void:\n\tvar file := FileAccess.open(_godot_log_file, FileAccess.READ)\n\tif file:\n\t\tfile.seek_end(0)\n\t\t_eof = file.get_length()\n\n\nfunc stop() -> void:\n\tpass\n\n\nfunc to_reports() -> Array[GdUnitReport]:\n\tvar reports_: Array[GdUnitReport] = []\n\tif _report_enabled:\n\t\treports_.assign(_entries.map(_to_report))\n\t_entries.clear()\n\treturn reports_\n\n\nstatic func _to_report(errorLog: ErrorLogEntry) -> GdUnitReport:\n\tvar failure := \"%s\\n\\t%s\\n%s %s\" % [\n\t\tGdAssertMessages._error(\"Godot Runtime Error !\"),\n\t\tGdAssertMessages._colored_value(errorLog._details),\n\t\tGdAssertMessages._error(\"Error:\"),\n\t\tGdAssertMessages._colored_value(errorLog._message)]\n\treturn GdUnitReport.new().create(GdUnitReport.ABORT, errorLog._line, failure)\n\n\nfunc scan(force_collect_reports := false) -> Array[ErrorLogEntry]:\n\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\tawait (Engine.get_main_loop() as SceneTree).physics_frame\n\t_entries.append_array(_collect_log_entries(force_collect_reports))\n\treturn _entries\n\n\nfunc erase_log_entry(entry: ErrorLogEntry) -> void:\n\t_entries.erase(entry)\n\n\nfunc _collect_log_entries(force_collect_reports: bool) -> Array[ErrorLogEntry]:\n\tvar file := FileAccess.open(_godot_log_file, FileAccess.READ)\n\tfile.seek(_eof)\n\tvar records := PackedStringArray()\n\twhile not file.eof_reached():\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\trecords.append(file.get_line())\n\tfile.seek_end(0)\n\t_eof = file.get_length()\n\tvar log_entries: Array[ErrorLogEntry]= []\n\tvar is_report_errors := force_collect_reports or _is_report_push_errors()\n\tvar is_report_script_errors := force_collect_reports or _is_report_script_errors()\n\tfor index in records.size():\n\t\tif force_collect_reports:\n\t\t\tlog_entries.append(ErrorLogEntry.extract_push_warning(records, index))\n\t\tif is_report_errors:\n\t\t\tlog_entries.append(ErrorLogEntry.extract_push_error(records, index))\n\t\tif is_report_script_errors:\n\t\t\tlog_entries.append(ErrorLogEntry.extract_error(records, index))\n\treturn log_entries.filter(func(value: ErrorLogEntry) -> bool: return value != null )\n\n\nfunc _is_reporting_enabled() -> bool:\n\treturn _is_report_script_errors() or _is_report_push_errors()\n\n\nfunc _is_report_push_errors() -> bool:\n\treturn GdUnitSettings.is_report_push_errors()\n\n\nfunc _is_report_script_errors() -> bool:\n\treturn GdUnitSettings.is_report_script_errors()\n"
  },
  {
    "path": "addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd.uid",
    "content": "uid://cwcqdtocb8qb6\n"
  },
  {
    "path": "addons/gdUnit4/src/mono/GdUnit4CSharpApi.cs",
    "content": "using System;\nusing System.Reflection;\nusing System.Linq;\n\nusing Godot;\nusing Godot.Collections;\nusing GdUnit4;\n\n\n// GdUnit4 GDScript - C# API wrapper\npublic partial class GdUnit4CSharpApi : Godot.GodotObject\n{\n\tprivate static Type? apiType;\n\n\tprivate static Type GetApiType()\n\t{\n\t\tif (apiType == null)\n\t\t{\n\t\t\tvar assembly = Assembly.Load(\"gdUnit4Api\");\n\t\t\tapiType = GdUnit4NetVersion() < new Version(4, 2, 2) ?\n\t\t\t\tassembly.GetType(\"GdUnit4.GdUnit4MonoAPI\") :\n\t\t\t\tassembly.GetType(\"GdUnit4.GdUnit4NetAPI\");\n\t\t\tGodot.GD.PrintS($\"GdUnit4CSharpApi type:{apiType} loaded.\");\n\t\t}\n\t\treturn apiType!;\n\t}\n\n\tprivate static Version GdUnit4NetVersion()\n\t{\n\t\tvar assembly = Assembly.Load(\"gdUnit4Api\");\n\t\treturn assembly.GetName().Version!;\n\t}\n\n\tprivate static T InvokeApiMethod<T>(string methodName, params object[] args)\n\t{\n\t\tvar method = GetApiType().GetMethod(methodName)!;\n\t\treturn (T)method.Invoke(null, args)!;\n\t}\n\n\tpublic static string Version() => GdUnit4NetVersion().ToString();\n\n\tpublic static bool IsTestSuite(string classPath) => InvokeApiMethod<bool>(\"IsTestSuite\", classPath);\n\n\tpublic static RefCounted Executor(Node listener) => InvokeApiMethod<RefCounted>(\"Executor\", listener);\n\n\tpublic static CsNode? ParseTestSuite(string classPath) => InvokeApiMethod<CsNode?>(\"ParseTestSuite\", classPath);\n\n\tpublic static Dictionary CreateTestSuite(string sourcePath, int lineNumber, string testSuitePath) =>\n\t\tInvokeApiMethod<Dictionary>(\"CreateTestSuite\", sourcePath, lineNumber, testSuitePath);\n}\n"
  },
  {
    "path": "addons/gdUnit4/src/mono/GdUnit4CSharpApi.cs.uid",
    "content": "uid://s8qxrcceo8ol\n"
  },
  {
    "path": "addons/gdUnit4/src/mono/GdUnit4CSharpApiLoader.gd",
    "content": "extends RefCounted\nclass_name GdUnit4CSharpApiLoader\n\n\nstatic func instance() -> Object:\n\treturn GdUnitSingleton.instance(\"GdUnit4CSharpApi\", func() -> Object:\n\t\tif not GdUnit4CSharpApiLoader.is_mono_supported():\n\t\t\treturn null\n\t\t@warning_ignore(\"unsafe_method_access\")\n\t\treturn load(\"res://addons/gdUnit4/src/mono/GdUnit4CSharpApi.cs\").new()\n\t)\n\n\nstatic func is_engine_version_supported(engine_version :int = Engine.get_version_info().hex) -> bool:\n\treturn engine_version >= 0x40200\n\n\n# test is Godot mono running\nstatic func is_mono_supported() -> bool:\n\treturn false\n\t# return ClassDB.class_exists(\"CSharpScript\") and is_engine_version_supported()\n\n\nstatic func version() -> String:\n\tif not GdUnit4CSharpApiLoader.is_mono_supported():\n\t\treturn \"unknown\"\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn instance().Version()\n\n\nstatic func create_test_suite(source_path :String, line_number :int, test_suite_path :String) -> GdUnitResult:\n\tif not GdUnit4CSharpApiLoader.is_mono_supported():\n\t\treturn  GdUnitResult.error(\"Can't create test suite. No C# support found.\")\n\t@warning_ignore(\"unsafe_method_access\")\n\tvar result: Dictionary = instance().CreateTestSuite(source_path, line_number, test_suite_path)\n\tif result.has(\"error\"):\n\t\treturn GdUnitResult.error(str(result.get(\"error\")))\n\treturn  GdUnitResult.success(result)\n\n\nstatic func is_test_suite(resource_path :String) -> bool:\n\tif not is_csharp_file(resource_path) or not GdUnit4CSharpApiLoader.is_mono_supported():\n\t\treturn false\n\n\tif resource_path.is_empty():\n\t\tif GdUnitSettings.is_report_push_errors():\n\t\t\tpush_error(\"Can't create test suite. Missing resource path.\")\n\t\treturn  false\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn instance().IsTestSuite(resource_path)\n\n\nstatic func parse_test_suite(source_path :String) -> Node:\n\tif not GdUnit4CSharpApiLoader.is_mono_supported():\n\t\tif GdUnitSettings.is_report_push_errors():\n\t\t\tpush_error(\"Can't create test suite. No c# support found.\")\n\t\treturn null\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn instance().ParseTestSuite(source_path)\n\n\nstatic func create_executor(listener :Node) -> RefCounted:\n\tif not GdUnit4CSharpApiLoader.is_mono_supported():\n\t\treturn null\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn instance().Executor(listener)\n\n\nstatic func is_csharp_file(resource_path :String) -> bool:\n\tvar ext := resource_path.get_extension()\n\treturn ext == \"cs\" and GdUnit4CSharpApiLoader.is_mono_supported()\n"
  },
  {
    "path": "addons/gdUnit4/src/mono/GdUnit4CSharpApiLoader.gd.uid",
    "content": "uid://cgi8w0pwk7qte\n"
  },
  {
    "path": "addons/gdUnit4/src/network/GdUnitServer.gd",
    "content": "@tool\nextends Node\n\n@onready var _server :GdUnitTcpServer = $TcpServer\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc _ready() -> void:\n\tvar result := _server.start()\n\tif result.is_error():\n\t\tpush_error(result.error_message())\n\t\treturn\n\tvar server_port :int = result.value()\n\tEngine.set_meta(\"gdunit_server_port\", server_port)\n\t_server.client_connected.connect(_on_client_connected)\n\t_server.client_disconnected.connect(_on_client_disconnected)\n\t_server.rpc_data.connect(_receive_rpc_data)\n\tGdUnitCommandHandler.instance().gdunit_runner_stop.connect(_on_gdunit_runner_stop)\n\n\nfunc _on_client_connected(client_id: int) -> void:\n\tGdUnitSignals.instance().gdunit_client_connected.emit(client_id)\n\n\nfunc _on_client_disconnected(client_id: int) -> void:\n\tGdUnitSignals.instance().gdunit_client_disconnected.emit(client_id)\n\n\nfunc _on_gdunit_runner_stop(client_id: int) -> void:\n\tif _server:\n\t\t_server.disconnect_client(client_id)\n\n\nfunc _receive_rpc_data(p_rpc: Variant) -> void:\n\tif p_rpc is RPCMessage:\n\t\tGdUnitSignals.instance().gdunit_message.emit(p_rpc.message())\n\t\treturn\n\tif p_rpc is RPCGdUnitEvent:\n\t\tGdUnitSignals.instance().gdunit_event.emit(p_rpc.event())\n\t\treturn\n\tif p_rpc is RPCGdUnitTestSuite:\n\t\tGdUnitSignals.instance().gdunit_add_test_suite.emit(p_rpc.dto())\n"
  },
  {
    "path": "addons/gdUnit4/src/network/GdUnitServer.gd.uid",
    "content": "uid://uqkvub7t7mo2\n"
  },
  {
    "path": "addons/gdUnit4/src/network/GdUnitServer.tscn",
    "content": "[gd_scene load_steps=3 format=3 uid=\"uid://cn5mp3tmi2gb1\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/gdUnit4/src/network/GdUnitServer.gd\" id=\"1\"]\n[ext_resource type=\"Script\" path=\"res://addons/gdUnit4/src/network/GdUnitTcpServer.gd\" id=\"2\"]\n\n[node name=\"Control\" type=\"Node\"]\nscript = ExtResource(\"1\")\n\n[node name=\"TcpServer\" type=\"Node\" parent=\".\"]\nscript = ExtResource(\"2\")\n"
  },
  {
    "path": "addons/gdUnit4/src/network/GdUnitServerConstants.gd",
    "content": "class_name GdUnitServerConstants\nextends RefCounted\n\nconst DEFAULT_SERVER_START_RETRY_TIMES :int = 5\nconst GD_TEST_SERVER_PORT :int = 31002\nconst JSON_RESPONSE_DELIMITER :String = \"<<JRD>>\"\n"
  },
  {
    "path": "addons/gdUnit4/src/network/GdUnitServerConstants.gd.uid",
    "content": "uid://cthgwh6tu0cro\n"
  },
  {
    "path": "addons/gdUnit4/src/network/GdUnitTask.gd",
    "content": "class_name GdUnitTask\nextends RefCounted\n\nconst TASK_NAME = \"task_name\"\nconst TASK_ARGS = \"task_args\"\n\nvar _task_name :String\nvar _fref :Callable\n\n\nfunc _init(task_name :String,instance :Object,func_name :String) -> void:\n\t_task_name = task_name\n\tif not instance.has_method(func_name):\n\t\tpush_error(\"Can't create GdUnitTask, Invalid func name '%s' for instance '%s'\" % [instance, func_name])\n\t_fref = Callable(instance, func_name)\n\n\nfunc name() -> String:\n\treturn _task_name\n\n\nfunc execute(args :Array) -> GdUnitResult:\n\tif args.is_empty():\n\t\treturn _fref.call()\n\treturn _fref.callv(args)\n"
  },
  {
    "path": "addons/gdUnit4/src/network/GdUnitTask.gd.uid",
    "content": "uid://bw4ri2xtvykse\n"
  },
  {
    "path": "addons/gdUnit4/src/network/GdUnitTcpClient.gd",
    "content": "class_name GdUnitTcpClient\nextends Node\n\nsignal connection_succeeded(message :String)\nsignal connection_failed(message :String)\n\n\nvar _host :String\nvar _port :int\nvar _client_id :int\nvar _connected :bool\nvar _stream :StreamPeerTCP\n\n\nfunc _ready() -> void:\n\t_connected = false\n\t_stream = StreamPeerTCP.new()\n\t_stream.set_big_endian(true)\n\n\nfunc stop() -> void:\n\tconsole(\"Client: disconnect from server\")\n\tif _stream != null:\n\t\trpc_send(RPCClientDisconnect.new().with_id(_client_id))\n\tif _stream != null:\n\t\t_stream.disconnect_from_host()\n\t_connected = false\n\n\nfunc start(host :String, port :int) -> GdUnitResult:\n\t_host = host\n\t_port = port\n\tif _connected:\n\t\treturn GdUnitResult.warn(\"Client already connected ... %s:%d\" % [_host, _port])\n\n\t# Connect client to server\n\tif _stream.get_status() != StreamPeerTCP.STATUS_CONNECTED:\n\t\tvar err := _stream.connect_to_host(host, port)\n\t\t#prints(\"connect_to_host\", host, port, err)\n\t\tif err != OK:\n\t\t\treturn GdUnitResult.error(\"GdUnit4: Can't establish client, error code: %s\" % err)\n\treturn GdUnitResult.success(\"GdUnit4: Client connected checked port %d\" % port)\n\n\nfunc _process(_delta :float) -> void:\n\tmatch _stream.get_status():\n\t\tStreamPeerTCP.STATUS_NONE:\n\t\t\treturn\n\n\t\tStreamPeerTCP.STATUS_CONNECTING:\n\t\t\tset_process(false)\n\t\t\t# wait until client is connected to server\n\t\t\tfor retry in 10:\n\t\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t\t_stream.poll()\n\t\t\t\tconsole(\"wait to connect ..\")\n\t\t\t\tif _stream.get_status() == StreamPeerTCP.STATUS_CONNECTING:\n\t\t\t\t\tawait get_tree().create_timer(0.500).timeout\n\t\t\t\tif _stream.get_status() == StreamPeerTCP.STATUS_CONNECTED:\n\t\t\t\t\tset_process(true)\n\t\t\t\t\treturn\n\t\t\tset_process(true)\n\t\t\t_stream.disconnect_from_host()\n\t\t\tconsole(\"connection failed\")\n\t\t\tconnection_failed.emit(\"Connect to TCP Server %s:%d faild!\" % [_host, _port])\n\n\t\tStreamPeerTCP.STATUS_CONNECTED:\n\t\t\tif not _connected:\n\t\t\t\tvar rpc_ :RPC = null\n\t\t\t\tset_process(false)\n\t\t\t\twhile rpc_ == null:\n\t\t\t\t\tawait get_tree().create_timer(0.500).timeout\n\t\t\t\t\trpc_ = rpc_receive()\n\t\t\t\tset_process(true)\n\t\t\t\t_client_id = (rpc_ as RPCClientConnect).client_id()\n\t\t\t\tconsole(\"Connected to Server: %d\" % _client_id)\n\t\t\t\tconnection_succeeded.emit(\"Connect to TCP Server %s:%d success.\" % [_host, _port])\n\t\t\t\t_connected = true\n\t\t\tprocess_rpc()\n\n\t\tStreamPeerTCP.STATUS_ERROR:\n\t\t\tconsole(\"connection failed\")\n\t\t\t_stream.disconnect_from_host()\n\t\t\tconnection_failed.emit(\"Connect to TCP Server %s:%d faild!\" % [_host, _port])\n\t\t\treturn\n\n\nfunc is_client_connected() -> bool:\n\treturn _connected\n\n\nfunc process_rpc() -> void:\n\tif _stream.get_available_bytes() > 0:\n\t\tvar rpc_ := rpc_receive()\n\t\tif rpc_ is RPCClientDisconnect:\n\t\t\tstop()\n\n\nfunc rpc_send(p_rpc :RPC) -> void:\n\tif _stream != null:\n\t\tvar data := GdUnitServerConstants.JSON_RESPONSE_DELIMITER + p_rpc.serialize() + GdUnitServerConstants.JSON_RESPONSE_DELIMITER\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\t_stream.put_data(data.to_utf8_buffer())\n\n\nfunc rpc_receive() -> RPC:\n\tif _stream != null:\n\t\twhile _stream.get_available_bytes() > 0:\n\t\t\tvar available_bytes := _stream.get_available_bytes()\n\t\t\tvar data := _stream.get_data(available_bytes)\n\t\t\tvar received_data: PackedByteArray = data[1]\n\t\t\t# data send by Godot has this magic header of 12 bytes\n\t\t\tvar header := Array(received_data.slice(0, 4))\n\t\t\tif header == [0, 0, 0, 124]:\n\t\t\t\treceived_data = received_data.slice(12, available_bytes)\n\t\t\tvar decoded := received_data.get_string_from_utf8()\n\t\t\tif decoded == \"\":\n\t\t\t\t#prints(\"decoded is empty\", available_bytes, received_data.get_string_from_utf8())\n\t\t\t\treturn null\n\t\t\treturn RPC.deserialize(decoded)\n\treturn null\n\n\nfunc console(_message :String) -> void:\n\t#prints(\"TCP Client:\", _message)\n\tpass\n\n\nfunc _on_connection_failed(message :String) -> void:\n\tconsole(\"connection faild: \" + message)\n\n\nfunc _on_connection_succeeded(message :String) -> void:\n\tconsole(\"connected: \" + message)\n"
  },
  {
    "path": "addons/gdUnit4/src/network/GdUnitTcpClient.gd.uid",
    "content": "uid://clr3slta45h4e\n"
  },
  {
    "path": "addons/gdUnit4/src/network/GdUnitTcpServer.gd",
    "content": "@tool\nclass_name GdUnitTcpServer\nextends Node\n\nsignal client_connected(client_id :int)\nsignal client_disconnected(client_id :int)\n@warning_ignore(\"unused_signal\")\nsignal rpc_data(rpc_data: RPC)\n\nvar _server :TCPServer\n\n\nclass TcpConnection extends Node:\n\tvar _id :int\n\t# we do use untyped here because we using a mock for testing and the static type is break the mock\n\t@warning_ignore(\"untyped_declaration\")\n\tvar _stream\n\tvar _readBuffer :String = \"\"\n\n\n\t@warning_ignore(\"unsafe_method_access\")\n\tfunc _init(p_server :Variant) -> void:\n\t\tassert(p_server is TCPServer)\n\t\t_stream = p_server.take_connection()\n\t\t_stream.set_big_endian(true)\n\t\t_id = _stream.get_instance_id()\n\t\trpc_send(RPCClientConnect.new().with_id(_id))\n\n\n\tfunc _ready() -> void:\n\t\tserver().client_connected.emit(_id)\n\n\n\tfunc close() -> void:\n\t\tif _stream != null:\n\t\t\t@warning_ignore(\"unsafe_method_access\")\n\t\t\t_stream.disconnect_from_host()\n\t\t\t_readBuffer = \"\"\n\t\t\t_stream = null\n\t\t\tqueue_free()\n\n\n\tfunc id() -> int:\n\t\treturn _id\n\n\n\tfunc server() -> GdUnitTcpServer:\n\t\treturn get_parent()\n\n\n\tfunc rpc_send(p_rpc: RPC) -> void:\n\t\t@warning_ignore(\"unsafe_method_access\")\n\t\t_stream.put_var(p_rpc.serialize(), true)\n\n\n\tfunc _process(_delta: float) -> void:\n\t\t@warning_ignore(\"unsafe_method_access\")\n\t\tif _stream == null or _stream.get_status() != StreamPeerTCP.STATUS_CONNECTED:\n\t\t\treturn\n\t\treceive_packages()\n\n\n\t@warning_ignore(\"unsafe_method_access\")\n\tfunc receive_packages() -> void:\n\t\tvar available_bytes :int = _stream.get_available_bytes()\n\t\tif available_bytes > 0:\n\t\t\tvar partial_data :Array = _stream.get_partial_data(available_bytes)\n\t\t\t# Check for read error.\n\t\t\tif partial_data[0] != OK:\n\t\t\t\tpush_error(\"Error getting data from stream: %s \" % partial_data[0])\n\t\t\t\treturn\n\t\t\telse:\n\t\t\t\tvar received_data: PackedByteArray = partial_data[1]\n\t\t\t\tfor package in _read_next_data_packages(received_data):\n\t\t\t\t\tvar rpc_ := RPC.deserialize(package)\n\t\t\t\t\tif rpc_ is RPCClientDisconnect:\n\t\t\t\t\t\tclose()\n\t\t\t\t\tserver().rpc_data.emit(rpc_)\n\n\n\tfunc _read_next_data_packages(data_package: PackedByteArray) -> PackedStringArray:\n\t\t_readBuffer += data_package.get_string_from_utf8()\n\t\tvar json_array := _readBuffer.split(GdUnitServerConstants.JSON_RESPONSE_DELIMITER)\n\t\t# We need to check if the current data is terminated by the delemiter (data packets can be split unspecifically).\n\t\t# If not, store the last part in _readBuffer and complete it on the next data packet that is received\n\t\tif not _readBuffer.ends_with(GdUnitServerConstants.JSON_RESPONSE_DELIMITER):\n\t\t\t_readBuffer = json_array[-1]\n\t\t\tjson_array.remove_at(json_array.size()-1)\n\t\telse:\n\t\t# Reset the buffer if a completely terminated packet was received\n\t\t\t_readBuffer = \"\"\n\t\t# remove empty packages\n\t\tfor index in json_array.size():\n\t\t\tif index < json_array.size() and json_array[index].is_empty():\n\t\t\t\tjson_array.remove_at(index)\n\t\treturn json_array\n\n\n\tfunc console(_message :String) -> void:\n\t\t#print_debug(\"TCP Connection:\", _message)\n\t\tpass\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc _ready() -> void:\n\t_server = TCPServer.new()\n\tclient_connected.connect(_on_client_connected)\n\tclient_disconnected.connect(_on_client_disconnected)\n\n\nfunc _notification(what: int) -> void:\n\tif what == NOTIFICATION_PREDELETE:\n\t\tstop()\n\n\nfunc start() -> GdUnitResult:\n\tvar server_port := GdUnitServerConstants.GD_TEST_SERVER_PORT\n\tvar err := OK\n\tfor retry in GdUnitServerConstants.DEFAULT_SERVER_START_RETRY_TIMES:\n\t\terr = _server.listen(server_port, \"127.0.0.1\")\n\t\tif err != OK:\n\t\t\tprints(\"GdUnit4: Can't establish server checked port: %d, Error: %s\" % [server_port, error_string(err)])\n\t\t\tserver_port += 1\n\t\t\tprints(\"GdUnit4: Retry (%d) ...\" % retry)\n\t\telse:\n\t\t\tbreak\n\tif err != OK:\n\t\tif err == ERR_ALREADY_IN_USE:\n\t\t\treturn GdUnitResult.error(\"GdUnit4: Can't establish server, the server is already in use. Error: %s, \" % error_string(err))\n\t\treturn GdUnitResult.error(\"GdUnit4: Can't establish server. Error: %s.\" % error_string(err))\n\tprints(\"GdUnit4: Test server successfully started checked port: %d\" % server_port)\n\treturn GdUnitResult.success(server_port)\n\n\nfunc stop() -> void:\n\tif _server:\n\t\t_server.stop()\n\tfor connection in get_children():\n\t\tif connection is TcpConnection:\n\t\t\t@warning_ignore(\"unsafe_method_access\")\n\t\t\tconnection.close()\n\t\t\tremove_child(connection)\n\t_server = null\n\n\nfunc disconnect_client(client_id: int) -> void:\n\tclient_disconnected.emit(client_id)\n\n\nfunc _process(_delta: float) -> void:\n\tif _server != null and not _server.is_listening():\n\t\treturn\n\t# check if connection is ready to be used\n\tif _server.is_connection_available():\n\t\tadd_child(TcpConnection.new(_server))\n\n\nfunc _on_client_connected(client_id: int) -> void:\n\tconsole(\"Client connected %d\" % client_id)\n\n\n@warning_ignore(\"unsafe_method_access\")\nfunc _on_client_disconnected(client_id: int) -> void:\n\tfor connection in get_children():\n\t\tif connection is TcpConnection and connection.id() == client_id:\n\t\t\tconnection.close()\n\t\t\tremove_child(connection)\n\n\nfunc console(_message: String) -> void:\n\t#print_debug(\"TCP Server:\", _message)\n\tpass\n"
  },
  {
    "path": "addons/gdUnit4/src/network/GdUnitTcpServer.gd.uid",
    "content": "uid://b2h4sugjmwjpa\n"
  },
  {
    "path": "addons/gdUnit4/src/network/rpc/RPC.gd",
    "content": "class_name RPC\nextends RefCounted\n\n\nfunc serialize() -> String:\n\treturn JSON.stringify(inst_to_dict(self))\n\n\n# using untyped version see comments below\nstatic func deserialize(json_value :String) -> Object:\n\tvar json := JSON.new()\n\tvar err := json.parse(json_value)\n\tif err != OK:\n\t\tpush_error(\"Can't deserialize JSON, error at line %d: %s \\n json: '%s'\" % [json.get_error_line(), json.get_error_message(), json_value])\n\t\treturn null\n\tvar result :Dictionary = json.get_data()\n\tif not typeof(result) == TYPE_DICTIONARY:\n\t\tpush_error(\"Can't deserialize JSON, error at line %d: %s \\n json: '%s'\" % [result.error_line, result.error_string, json_value])\n\t\treturn null\n\treturn dict_to_inst(result)\n\n# this results in orpan node, for more details https://github.com/godotengine/godot/issues/50069\n#func deserialize2(data :Dictionary) -> RPC:\n#\treturn  dict_to_inst(data) as RPC\n"
  },
  {
    "path": "addons/gdUnit4/src/network/rpc/RPC.gd.uid",
    "content": "uid://b16y0r2ie7ak7\n"
  },
  {
    "path": "addons/gdUnit4/src/network/rpc/RPCClientConnect.gd",
    "content": "class_name RPCClientConnect\nextends RPC\n\nvar _client_id :int\n\n\nfunc with_id(p_client_id :int) -> RPCClientConnect:\n\t_client_id = p_client_id\n\treturn self\n\n\nfunc client_id() -> int:\n\treturn _client_id\n"
  },
  {
    "path": "addons/gdUnit4/src/network/rpc/RPCClientConnect.gd.uid",
    "content": "uid://duroahu14cy25\n"
  },
  {
    "path": "addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd",
    "content": "class_name RPCClientDisconnect\nextends RPC\n\nvar _client_id :int\n\n\nfunc with_id(p_client_id :int) -> RPCClientDisconnect:\n\t_client_id = p_client_id\n\treturn self\n\n\nfunc client_id() -> int:\n\treturn _client_id\n"
  },
  {
    "path": "addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd.uid",
    "content": "uid://c3shu7mphjbx8\n"
  },
  {
    "path": "addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd",
    "content": "class_name RPCGdUnitEvent\nextends RPC\n\nvar _event :Dictionary\n\n\nstatic func of(p_event :GdUnitEvent) -> RPCGdUnitEvent:\n\tvar rpc := RPCGdUnitEvent.new()\n\trpc._event = p_event.serialize()\n\treturn rpc\n\n\nfunc event() -> GdUnitEvent:\n\treturn GdUnitEvent.new().deserialize(_event)\n\n\nfunc _to_string() -> String:\n\treturn \"RPCGdUnitEvent: \" + str(_event)\n"
  },
  {
    "path": "addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd.uid",
    "content": "uid://bgewbgn7vm7n\n"
  },
  {
    "path": "addons/gdUnit4/src/network/rpc/RPCGdUnitTestSuite.gd",
    "content": "class_name RPCGdUnitTestSuite\nextends RPC\n\nvar _data :Dictionary\n\n\nstatic func of(test_suite :Node) -> RPCGdUnitTestSuite:\n\tvar rpc := RPCGdUnitTestSuite.new()\n\trpc._data = GdUnitTestSuiteDto.new().serialize(test_suite)\n\treturn rpc\n\n\nfunc dto() -> GdUnitResourceDto:\n\treturn GdUnitTestSuiteDto.new().deserialize(_data)\n\n\nfunc _to_string() -> String:\n\treturn \"RPCGdUnitTestSuite: \" + str(_data)\n"
  },
  {
    "path": "addons/gdUnit4/src/network/rpc/RPCGdUnitTestSuite.gd.uid",
    "content": "uid://4w4amdhelrhv\n"
  },
  {
    "path": "addons/gdUnit4/src/network/rpc/RPCMessage.gd",
    "content": "class_name RPCMessage\nextends RPC\n\nvar _message :String\n\n\nstatic func of(p_message :String) -> RPCMessage:\n\tvar rpc := RPCMessage.new()\n\trpc._message = p_message\n\treturn rpc\n\n\nfunc message() -> String:\n\treturn _message\n\n\nfunc _to_string() -> String:\n\treturn \"RPCMessage: \" + _message\n"
  },
  {
    "path": "addons/gdUnit4/src/network/rpc/RPCMessage.gd.uid",
    "content": "uid://dv2wy3x61gcxs\n"
  },
  {
    "path": "addons/gdUnit4/src/network/rpc/dtos/GdUnitResourceDto.gd",
    "content": "class_name GdUnitResourceDto\nextends Resource\n\nvar _name :String\nvar _path :String\n\n\nfunc serialize(resource :Node) -> Dictionary:\n\tvar serialized := Dictionary()\n\tserialized[\"name\"] = resource.get_name()\n\t@warning_ignore(\"unsafe_method_access\")\n\tserialized[\"resource_path\"] = resource.ResourcePath()\n\treturn serialized\n\n\nfunc deserialize(data :Dictionary) -> GdUnitResourceDto:\n\t_name = data.get(\"name\", \"n.a.\")\n\t_path = data.get(\"resource_path\", \"\")\n\treturn self\n\n\nfunc name() -> String:\n\treturn _name\n\n\nfunc path() -> String:\n\treturn _path\n"
  },
  {
    "path": "addons/gdUnit4/src/network/rpc/dtos/GdUnitResourceDto.gd.uid",
    "content": "uid://cp6kj8q0ft0dd\n"
  },
  {
    "path": "addons/gdUnit4/src/network/rpc/dtos/GdUnitTestCaseDto.gd",
    "content": "class_name GdUnitTestCaseDto\nextends GdUnitResourceDto\n\nvar _line_number :int = -1\nvar _script_path: String\nvar _test_case_names :PackedStringArray = []\n\n\n@warning_ignore(\"unsafe_method_access\")\nfunc serialize(test_case :Node) -> Dictionary:\n\tvar serialized := super.serialize(test_case)\n\tif test_case.has_method(\"line_number\"):\n\t\tserialized[\"line_number\"] = test_case.line_number()\n\telse:\n\t\tserialized[\"line_number\"] = test_case.get(\"LineNumber\")\n\tif test_case.has_method(\"script_path\"):\n\t\tserialized[\"script_path\"] = test_case.script_path()\n\telse:\n\t\t# TODO 'script_path' needs to be implement in c# the the\n\t\t# serialized[\"script_path\"] = test_case.get(\"ScriptPath\")\n\t\tserialized[\"script_path\"] = serialized[\"resource_path\"]\n\tif test_case.has_method(\"test_case_names\"):\n\t\tserialized[\"test_case_names\"] = test_case.test_case_names()\n\telif test_case.has_method(\"TestCaseNames\"):\n\t\tserialized[\"test_case_names\"] = test_case.TestCaseNames()\n\treturn serialized\n\n\nfunc deserialize(data :Dictionary) -> GdUnitTestCaseDto:\n\t@warning_ignore(\"return_value_discarded\")\n\tsuper.deserialize(data)\n\t_line_number = data.get(\"line_number\", -1)\n\t_script_path = data.get(\"script_path\", data.get(\"resource_path\", \"\"))\n\t_test_case_names = data.get(\"test_case_names\", [])\n\treturn self\n\n\nfunc line_number() -> int:\n\treturn _line_number\n\n\nfunc script_path() -> String:\n\treturn _script_path\n\n\nfunc test_case_names() -> PackedStringArray:\n\treturn _test_case_names\n"
  },
  {
    "path": "addons/gdUnit4/src/network/rpc/dtos/GdUnitTestCaseDto.gd.uid",
    "content": "uid://inmdrjv1vkuh\n"
  },
  {
    "path": "addons/gdUnit4/src/network/rpc/dtos/GdUnitTestSuiteDto.gd",
    "content": "class_name GdUnitTestSuiteDto\nextends GdUnitResourceDto\n\n\n# Dictionary[String, GdUnitTestCaseDto]\nvar _test_cases_by_name := Dictionary()\n\n\nstatic func of(test_suite :Node) -> GdUnitTestSuiteDto:\n\tvar dto := GdUnitTestSuiteDto.new()\n\treturn dto.deserialize(dto.serialize(test_suite))\n\n\nfunc serialize(test_suite :Node) -> Dictionary:\n\tvar serialized := super.serialize(test_suite)\n\tvar test_cases_ := Array()\n\tserialized[\"test_cases\"] = test_cases_\n\tfor test_case in test_suite.get_children():\n\t\ttest_cases_.append(GdUnitTestCaseDto.new().serialize(test_case))\n\treturn serialized\n\n\nfunc deserialize(data :Dictionary) -> GdUnitResourceDto:\n\t@warning_ignore(\"return_value_discarded\")\n\tsuper.deserialize(data)\n\tvar test_cases_ :Array = data.get(\"test_cases\", [])\n\tfor test_case :Dictionary in test_cases_:\n\t\tadd_test_case(GdUnitTestCaseDto.new().deserialize(test_case))\n\treturn self\n\n\nfunc add_test_case(test_case :GdUnitTestCaseDto) -> void:\n\t_test_cases_by_name[test_case.name()] = test_case\n\n\nfunc test_case_count() -> int:\n\treturn _test_cases_by_name.size()\n\n\nfunc test_cases() -> Array[GdUnitTestCaseDto]:\n\tvar test_cases_ :Array[GdUnitTestCaseDto] = []\n\ttest_cases_.append_array(_test_cases_by_name.values())\n\treturn test_cases_\n"
  },
  {
    "path": "addons/gdUnit4/src/network/rpc/dtos/GdUnitTestSuiteDto.gd.uid",
    "content": "uid://csc8w0t3uyah4\n"
  },
  {
    "path": "addons/gdUnit4/src/report/GdUnitByPathReport.gd",
    "content": "class_name GdUnitByPathReport\nextends GdUnitReportSummary\n\n\nfunc _init(p_path :String, report_summaries :Array[GdUnitReportSummary]) -> void:\n\t_resource_path = p_path\n\t_reports = report_summaries\n\n\n# -> Dictionary[String, Array[GdUnitReportSummary]]\nstatic func sort_reports_by_path(report_summaries :Array[GdUnitReportSummary]) -> Dictionary:\n\tvar by_path := Dictionary()\n\tfor report in report_summaries:\n\t\tvar suite_path :String = ProjectSettings.localize_path(report.path())\n\t\tvar suite_report :Array[GdUnitReportSummary] = by_path.get(suite_path, [] as Array[GdUnitReportSummary])\n\t\tsuite_report.append(report)\n\t\tby_path[suite_path] = suite_report\n\treturn by_path\n\n\nfunc path() -> String:\n\treturn _resource_path.replace(\"res://\", \"\")\n\n\nfunc create_record(report_link :String) -> String:\n\treturn GdUnitHtmlPatterns.build(GdUnitHtmlPatterns.TABLE_RECORD_PATH, self, report_link)\n\n\nfunc write(report_dir :String) -> String:\n\tcalculate_summary()\n\tvar template := GdUnitHtmlPatterns.load_template(\"res://addons/gdUnit4/src/report/template/folder_report.html\")\n\tvar path_report := GdUnitHtmlPatterns.build(template, self, \"\")\n\tpath_report = apply_testsuite_reports(report_dir, path_report, _reports)\n\n\tvar output_path := \"%s/path/%s.html\" % [report_dir, path().replace(\"/\", \".\")]\n\tvar dir := output_path.get_base_dir()\n\tif not DirAccess.dir_exists_absolute(dir):\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tDirAccess.make_dir_recursive_absolute(dir)\n\tFileAccess.open(output_path, FileAccess.WRITE).store_string(path_report)\n\treturn output_path\n\n\nfunc apply_testsuite_reports(report_dir :String, template :String, test_suite_reports :Array[GdUnitReportSummary]) -> String:\n\tvar table_records := PackedStringArray()\n\tfor report:GdUnitTestSuiteReport in test_suite_reports:\n\t\tvar report_link := report.output_path(report_dir).replace(report_dir, \"..\")\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\ttable_records.append(report.create_record(report_link))\n\treturn template.replace(GdUnitHtmlPatterns.TABLE_BY_TESTSUITES, \"\\n\".join(table_records))\n\n\nfunc calculate_summary() -> void:\n\tfor report:GdUnitTestSuiteReport in get_reports():\n\t\t_error_count += report.error_count()\n\t\t_failure_count += report.failure_count()\n\t\t_orphan_count += report.orphan_count()\n\t\t_skipped_count += report.skipped_count()\n\t\t_flaky_count += report.flaky_count()\n\t\t_duration += report.duration()\n"
  },
  {
    "path": "addons/gdUnit4/src/report/GdUnitByPathReport.gd.uid",
    "content": "uid://3eingmkh2y7f\n"
  },
  {
    "path": "addons/gdUnit4/src/report/GdUnitHtmlPatterns.gd",
    "content": "class_name GdUnitHtmlPatterns\nextends RefCounted\n\nconst TABLE_RECORD_TESTSUITE = \"\"\"\n\t\t\t\t\t\t\t\t<tr class=\"${report_state}\">\n\t\t\t\t\t\t\t\t\t<td><a href=${report_link}>${testsuite_name}</a></td>\n\t\t\t\t\t\t\t\t\t<td><span class=\"status status-${report_state}\">${report_state_label}</span></td>\n\t\t\t\t\t\t\t\t\t<td>${test_count}</td>\n\t\t\t\t\t\t\t\t\t<td>${skipped_count}</td>\n\t\t\t\t\t\t\t\t\t<td>${flaky_count}</td>\n\t\t\t\t\t\t\t\t\t<td>${failure_count}</td>\n\t\t\t\t\t\t\t\t\t<td>${orphan_count}</td>\n\t\t\t\t\t\t\t\t\t<td>${duration}</td>\n\t\t\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t\t\t<div class=\"status-bar\">\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"status-bar-column status-skipped\" style=\"width: ${skipped-percent};\"></div>\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"status-bar-column status-passed\" style=\"width: ${passed-percent};\"></div>\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"status-bar-column status-flaky\" style=\"width: ${flaky-percent};\"></div>\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"status-bar-column status-error\" style=\"width: ${error-percent};\"></div>\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"status-bar-column status-failed\" style=\"width: ${failed-percent};\"></div>\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"status-bar-column status-warning\" style=\"width: ${warning-percent};\"></div>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t</tr>\n\"\"\"\n\nconst TABLE_RECORD_PATH = \"\"\"\n\t\t\t\t\t\t\t\t<tr class=\"${report_state}\">\n\t\t\t\t\t\t\t\t\t<td><a class=\"${report_state}\" href=\"${report_link}\">${path}</a></td>\n\t\t\t\t\t\t\t\t\t<td><span class=\"status status-${report_state}\">${report_state_label}</span></td>\n\t\t\t\t\t\t\t\t\t<td>${test_count}</td>\n\t\t\t\t\t\t\t\t\t<td>${skipped_count}</td>\n\t\t\t\t\t\t\t\t\t<td>${flaky_count}</td>\n\t\t\t\t\t\t\t\t\t<td>${failure_count}</td>\n\t\t\t\t\t\t\t\t\t<td>${orphan_count}</td>\n\t\t\t\t\t\t\t\t\t<td>${duration}</td>\n\t\t\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t\t\t<div class=\"status-bar\">\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"status-bar-column status-skipped\" style=\"width: ${passed-skipped};\"></div>\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"status-bar-column status-passed\" style=\"width: ${passed-percent};\"></div>\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"status-bar-column status-flaky\" style=\"width: ${flaky-percent};\"></div>\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"status-bar-column status-error\" style=\"width: ${error-percent};\"></div>\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"status-bar-column status-failed\" style=\"width: ${failed-percent};\"></div>\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"status-bar-column status-warning\" style=\"width: ${warning-percent};\"></div>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t</tr>\n\"\"\"\n\n\nconst TABLE_REPORT_TESTSUITE = \"\"\"\n\t\t\t\t\t\t\t\t<tr class=\"${report_state}\">\n\t\t\t\t\t\t\t\t\t<td>TestSuite hooks</td>\n\t\t\t\t\t\t\t\t\t<td>n/a</td>\n\t\t\t\t\t\t\t\t\t<td>${orphan_count}</td>\n\t\t\t\t\t\t\t\t\t<td>${duration}</td>\n\t\t\t\t\t\t\t\t\t<td class=\"report-column\">\n\t\t\t\t\t\t\t\t\t\t<pre>\n${failure-report}\n\t\t\t\t\t\t\t\t\t\t</pre>\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t</tr>\n\"\"\"\n\n\nconst TABLE_RECORD_TESTCASE = \"\"\"\n\t\t\t\t\t\t\t\t<tr class=\"testcase-group\">\n\t\t\t\t\t\t\t\t\t<td>${testcase_name}</td>\n\t\t\t\t\t\t\t\t\t<td><span class=\"status status-${report_state}\">${report_state_label}</span></td>\n\t\t\t\t\t\t\t\t\t<td>${skipped_count}</td>\n\t\t\t\t\t\t\t\t\t<td>${orphan_count}</td>\n\t\t\t\t\t\t\t\t\t<td>${duration}</td>\n\t\t\t\t\t\t\t\t\t<td class=\"report-column\">\n\t\t\t\t\t\t\t\t\t\t<pre>\n${failure-report}\n\t\t\t\t\t\t\t\t\t\t</pre>\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t</tr>\n\"\"\"\n\nconst TABLE_BY_PATHS = \"${report_table_paths}\"\nconst TABLE_BY_TESTSUITES = \"${report_table_testsuites}\"\nconst TABLE_BY_TESTCASES = \"${report_table_tests}\"\n\n# the report state success, error, warning\nconst REPORT_STATE = \"${report_state}\"\nconst REPORT_STATE_LABEL = \"${report_state_label}\"\nconst PATH = \"${path}\"\nconst RESOURCE_PATH = \"${resource_path}\"\nconst TESTSUITE_COUNT = \"${suite_count}\"\nconst TESTCASE_COUNT = \"${test_count}\"\nconst FAILURE_COUNT = \"${failure_count}\"\nconst FLAKY_COUNT = \"${flaky_count}\"\nconst SKIPPED_COUNT = \"${skipped_count}\"\nconst ORPHAN_COUNT = \"${orphan_count}\"\nconst DURATION = \"${duration}\"\nconst FAILURE_REPORT = \"${failure-report}\"\nconst SUCCESS_PERCENT = \"${success_percent}\"\n\n\nconst QUICK_STATE_SKIPPED = \"${skipped-percent}\"\nconst QUICK_STATE_PASSED = \"${passed-percent}\"\nconst QUICK_STATE_FLAKY = \"${flaky-percent}\"\nconst QUICK_STATE_ERROR = \"${error-percent}\"\nconst QUICK_STATE_FAILED = \"${failed-percent}\"\nconst QUICK_STATE_WARNING = \"${warning-percent}\"\n\nconst TESTSUITE_NAME = \"${testsuite_name}\"\nconst TESTCASE_NAME = \"${testcase_name}\"\nconst REPORT_LINK = \"${report_link}\"\nconst BREADCRUMP_PATH_LINK = \"${breadcrumb_path_link}\"\nconst BUILD_DATE = \"${buid_date}\"\n\n\nstatic func current_date() -> String:\n\treturn Time.get_datetime_string_from_system(true, true)\n\n\nstatic func build(template: String, report: GdUnitReportSummary, report_link: String) -> String:\n\treturn template\\\n\t\t.replace(PATH, get_report_path(report))\\\n\t\t.replace(BREADCRUMP_PATH_LINK, get_path_as_link(report))\\\n\t\t.replace(RESOURCE_PATH, report.resource_path())\\\n\t\t.replace(TESTSUITE_NAME, report.name_html_encoded())\\\n\t\t.replace(TESTSUITE_COUNT, str(report.suite_count()))\\\n\t\t.replace(TESTCASE_COUNT, str(report.test_count()))\\\n\t\t.replace(FAILURE_COUNT, str(report.error_count() + report.failure_count()))\\\n\t\t.replace(FLAKY_COUNT, str(report.flaky_count()))\\\n\t\t.replace(SKIPPED_COUNT, str(report.skipped_count()))\\\n\t\t.replace(ORPHAN_COUNT, str(report.orphan_count()))\\\n\t\t.replace(DURATION, LocalTime.elapsed(report.duration()))\\\n\t\t.replace(SUCCESS_PERCENT, report.calculate_succes_rate(report.test_count(), report.error_count(), report.failure_count()))\\\n\t\t.replace(REPORT_STATE, report.report_state().to_lower())\\\n\t\t.replace(REPORT_STATE_LABEL, report.report_state())\\\n\t\t.replace(QUICK_STATE_SKIPPED, calculate_percentage(report.test_count(), report.skipped_count()))\\\n\t\t.replace(QUICK_STATE_PASSED, calculate_percentage(report.test_count(), report.success_count()))\\\n\t\t.replace(QUICK_STATE_FLAKY, calculate_percentage(report.test_count(), report.flaky_count()))\\\n\t\t.replace(QUICK_STATE_ERROR, calculate_percentage(report.test_count(), report.error_count()))\\\n\t\t.replace(QUICK_STATE_FAILED, calculate_percentage(report.test_count(), report.failure_count()))\\\n\t\t.replace(QUICK_STATE_WARNING, calculate_percentage(report.test_count(), 0))\\\n\t\t.replace(REPORT_LINK, report_link)\\\n\t\t.replace(BUILD_DATE, current_date())\n\n\nstatic func load_template(template_name :String) -> String:\n\treturn FileAccess.open(template_name, FileAccess.READ).get_as_text()\n\n\nstatic func get_path_as_link(report: GdUnitReportSummary) -> String:\n\treturn \"../path/%s.html\" % report.path().replace(\"/\", \".\")\n\n\nstatic func get_report_path(report: GdUnitReportSummary) -> String:\n\tvar path := report.path()\n\tif path.is_empty():\n\t\treturn \"/\"\n\treturn path\n\n\nstatic func calculate_percentage(p_test_count: int, count: int) -> String:\n\tif count <= 0:\n\t\treturn \"0%\"\n\treturn \"%d\" % (( 0 if count < 0 else count) * 100.0 / p_test_count) + \"%\"\n"
  },
  {
    "path": "addons/gdUnit4/src/report/GdUnitHtmlPatterns.gd.uid",
    "content": "uid://s8jig2jwcir\n"
  },
  {
    "path": "addons/gdUnit4/src/report/GdUnitHtmlReport.gd",
    "content": "class_name GdUnitHtmlReport\nextends GdUnitReportSummary\n\nconst REPORT_DIR_PREFIX = \"report_\"\n\nvar _report_path :String\nvar _iteration :int\n\n\nfunc _init(report_path :String, max_reports: int) -> void:\n\tif max_reports > 1:\n\t\t_iteration = GdUnitFileAccess.find_last_path_index(report_path, REPORT_DIR_PREFIX) + 1\n\telse:\n\t\t_iteration = 1\n\t_report_path = \"%s/%s%d\" % [report_path, REPORT_DIR_PREFIX, _iteration]\n\t@warning_ignore(\"return_value_discarded\")\n\tDirAccess.make_dir_recursive_absolute(_report_path)\n\n\nfunc add_testsuite_report(p_resource_path: String, p_suite_name: String, p_test_count: int) -> void:\n\t_reports.append(GdUnitTestSuiteReport.new(p_resource_path, p_suite_name, p_test_count))\n\n\n@warning_ignore(\"shadowed_variable\")\nfunc add_testcase(resource_path :String, suite_name :String, test_name: String) -> void:\n\tfor report:GdUnitTestSuiteReport in _reports:\n\t\tif report.resource_path() == resource_path:\n\t\t\tvar test_report := GdUnitTestCaseReport.new(resource_path, suite_name, test_name)\n\t\t\treport.add_or_create_test_report(test_report)\n\n\nfunc add_testsuite_reports(\n\tp_resource_path :String,\n\tp_error_count :int,\n\tp_failure_count :int,\n\tp_orphan_count :int,\n\tp_duration :int,\n\tp_reports :Array = []) -> void:\n\n\tfor report:GdUnitTestSuiteReport in _reports:\n\t\tif report.resource_path() == p_resource_path:\n\t\t\treport.set_reports(p_reports)\n\tupdate_summary_counters(p_error_count, p_failure_count, p_orphan_count, 0, 0, p_duration)\n\n\nfunc add_testcase_reports(\n\tp_resource_path: String,\n\tp_test_name: String,\n\tp_reports: Array[GdUnitReport]) -> void:\n\n\tfor report:GdUnitTestSuiteReport in _reports:\n\t\tif report.resource_path() == p_resource_path:\n\t\t\treport.add_testcase_reports(p_test_name, p_reports)\n\n\nfunc update_testsuite_counters(\n\tp_resource_path :String,\n\tp_error_count: int,\n\tp_failure_count: int,\n\tp_orphan_count: int,\n\tp_is_skipped: bool,\n\tp_is_flaky: bool,\n\tp_duration: int) -> void:\n\n\tfor report:GdUnitTestSuiteReport in _reports:\n\t\tif report.resource_path() == p_resource_path:\n\t\t\treport.update_testsuite_counters(p_error_count, p_failure_count, p_orphan_count, p_is_skipped, p_is_flaky, p_duration)\n\tupdate_summary_counters(p_error_count, p_failure_count, p_orphan_count, p_is_skipped, p_is_flaky, 0)\n\n\nfunc set_testcase_counters(\n\tp_resource_path: String,\n\tp_test_name: String,\n\tp_error_count: int,\n\tp_failure_count: int,\n\tp_orphan_count: int,\n\tp_is_skipped: bool,\n\tp_is_flaky: bool,\n\tp_duration: int) -> void:\n\n\tfor report:GdUnitTestSuiteReport in _reports:\n\t\tif report.resource_path() == p_resource_path:\n\t\t\treport.set_testcase_counters(p_test_name, p_error_count, p_failure_count, p_orphan_count,\n\t\t\t\tp_is_skipped, p_is_flaky, p_duration)\n\n\nfunc update_summary_counters(\n\tp_error_count: int,\n\tp_failure_count: int,\n\tp_orphan_count: int,\n\tp_is_skipped: bool,\n\tp_is_flaky: bool,\n\tp_duration: int) -> void:\n\n\t_error_count += p_error_count\n\t_failure_count += p_failure_count\n\t_orphan_count += p_orphan_count\n\t_skipped_count += p_is_skipped as int\n\t_flaky_count += p_is_flaky as int\n\t_duration += p_duration\n\n\nfunc write() -> String:\n\tvar template := GdUnitHtmlPatterns.load_template(\"res://addons/gdUnit4/src/report/template/index.html\")\n\tvar to_write := GdUnitHtmlPatterns.build(template, self, \"\")\n\tto_write = apply_path_reports(_report_path, to_write, _reports)\n\tto_write = apply_testsuite_reports(_report_path, to_write, _reports)\n\t# write report\n\tvar index_file := \"%s/index.html\" % _report_path\n\tFileAccess.open(index_file, FileAccess.WRITE).store_string(to_write)\n\t@warning_ignore(\"return_value_discarded\")\n\tGdUnitFileAccess.copy_directory(\"res://addons/gdUnit4/src/report/template/css/\", _report_path + \"/css\")\n\treturn index_file\n\n\nfunc delete_history(max_reports :int) -> int:\n\treturn GdUnitFileAccess.delete_path_index_lower_equals_than(_report_path.get_base_dir(), REPORT_DIR_PREFIX, _iteration-max_reports)\n\n\nfunc apply_path_reports(report_dir :String, template :String, report_summaries :Array) -> String:\n\t#Dictionary[String, Array[GdUnitReportSummary]]\n\tvar path_report_mapping := GdUnitByPathReport.sort_reports_by_path(report_summaries)\n\tvar table_records := PackedStringArray()\n\tvar paths :Array[String] = []\n\tpaths.append_array(path_report_mapping.keys())\n\tpaths.sort()\n\tfor report_path in paths:\n\t\tvar reports: Array[GdUnitReportSummary] = path_report_mapping.get(report_path)\n\t\tvar report := GdUnitByPathReport.new(report_path, reports)\n\t\tvar report_link :String = report.write(report_dir).replace(report_dir, \".\")\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\ttable_records.append(report.create_record(report_link))\n\treturn template.replace(GdUnitHtmlPatterns.TABLE_BY_PATHS, \"\\n\".join(table_records))\n\n\nfunc apply_testsuite_reports(report_dir: String, template: String, test_suite_reports: Array[GdUnitReportSummary]) -> String:\n\tvar table_records := PackedStringArray()\n\tfor report: GdUnitTestSuiteReport in test_suite_reports:\n\t\tvar report_link :String = report.write(report_dir).replace(report_dir, \".\")\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\ttable_records.append(report.create_record(report_link) as String)\n\treturn template.replace(GdUnitHtmlPatterns.TABLE_BY_TESTSUITES, \"\\n\".join(table_records))\n\n\nfunc iteration() -> int:\n\treturn _iteration\n"
  },
  {
    "path": "addons/gdUnit4/src/report/GdUnitHtmlReport.gd.uid",
    "content": "uid://c833to80rjhcy\n"
  },
  {
    "path": "addons/gdUnit4/src/report/GdUnitReportSummary.gd",
    "content": "class_name GdUnitReportSummary\nextends RefCounted\n\nconst GdUnitTools := preload(\"res://addons/gdUnit4/src/core/GdUnitTools.gd\")\n\nconst CHARACTERS_TO_ENCODE := {\n\t'<' : '&lt;',\n\t'>' : '&gt;'\n}\n\nvar _resource_path :String\nvar _name :String\nvar _test_count := 0\nvar _failure_count := 0\nvar _error_count := 0\nvar _orphan_count := 0\nvar _skipped_count := 0\nvar _flaky_count := 0\nvar _duration := 0\nvar _reports :Array[GdUnitReportSummary] = []\n\nfunc name() -> String:\n\treturn _name\n\n\nfunc name_html_encoded() -> String:\n\treturn html_encode(_name)\n\n\nfunc path() -> String:\n\treturn _resource_path.get_base_dir().replace(\"res://\", \"\")\n\n\nfunc resource_path() -> String:\n\treturn _resource_path\n\n\nfunc suite_count() -> int:\n\treturn _reports.size()\n\n\nfunc suite_executed_count() -> int:\n\tvar executed := _reports.size()\n\tfor report in _reports:\n\t\tif report.test_count() == report.skipped_count():\n\t\t\texecuted -= 1\n\treturn executed\n\n\nfunc test_count() -> int:\n\tvar count := _test_count\n\tfor report in _reports:\n\t\tcount += report.test_count()\n\treturn count\n\n\nfunc test_executed_count() -> int:\n\treturn test_count() - skipped_count()\n\n\nfunc success_count() -> int:\n\treturn test_count() - error_count() - failure_count() - flaky_count() - skipped_count()\n\n\nfunc error_count() -> int:\n\treturn _error_count\n\n\nfunc failure_count() -> int:\n\treturn _failure_count\n\n\nfunc skipped_count() -> int:\n\treturn _skipped_count\n\n\nfunc flaky_count() -> int:\n\treturn _flaky_count\n\n\nfunc orphan_count() -> int:\n\treturn _orphan_count\n\n\nfunc duration() -> int:\n\treturn _duration\n\n\nfunc get_reports() -> Array:\n\treturn _reports\n\n\nfunc add_report(report :GdUnitReportSummary) -> void:\n\t_reports.append(report)\n\n\nfunc report_state() -> String:\n\treturn calculate_state(error_count(), failure_count(), orphan_count(), flaky_count(), skipped_count())\n\n\nfunc succes_rate() -> String:\n\treturn calculate_succes_rate(test_count(), error_count(), failure_count())\n\n\nfunc calculate_state(p_error_count :int, p_failure_count :int, p_orphan_count :int, p_flaky_count: int, p_skipped_count: int) -> String:\n\tif p_skipped_count > 0:\n\t\treturn \"SKIPPED\"\n\tif p_error_count > 0:\n\t\treturn \"ERROR\"\n\tif p_failure_count > 0:\n\t\treturn \"FAILED\"\n\tif p_flaky_count > 0:\n\t\treturn \"FLAKY\"\n\tif p_orphan_count > 0:\n\t\treturn \"WARNING\"\n\treturn \"PASSED\"\n\n\nfunc calculate_succes_rate(p_test_count :int, p_error_count :int, p_failure_count :int) -> String:\n\tif p_failure_count == 0:\n\t\treturn \"100%\"\n\tvar count := p_test_count-p_failure_count-p_error_count\n\tif count < 0:\n\t\treturn \"0%\"\n\treturn \"%d\" % (( 0 if count < 0 else count) * 100.0 / p_test_count) + \"%\"\n\n\nfunc create_summary(_report_dir :String) -> String:\n\treturn \"\"\n\n\nfunc html_encode(value: String) -> String:\n\tfor key: String in CHARACTERS_TO_ENCODE.keys():\n\t\t@warning_ignore(\"unsafe_cast\")\n\t\tvalue = value.replace(key, CHARACTERS_TO_ENCODE[key] as String)\n\treturn value\n\n\nfunc convert_rtf_to_html(bbcode :String) -> String:\n\treturn GdUnitTools.richtext_normalize(bbcode)\n"
  },
  {
    "path": "addons/gdUnit4/src/report/GdUnitReportSummary.gd.uid",
    "content": "uid://b5dq1ddqag8ku\n"
  },
  {
    "path": "addons/gdUnit4/src/report/GdUnitTestCaseReport.gd",
    "content": "class_name GdUnitTestCaseReport\nextends GdUnitReportSummary\n\nvar _suite_name :String\nvar _failure_reports :Array[GdUnitReport]\n\n\n@warning_ignore(\"shadowed_variable\")\nfunc _init(p_resource_path: String, p_suite_name: String, p_test_name: String) -> void:\n\t_resource_path = p_resource_path\n\t_suite_name = p_suite_name\n\t_name = p_test_name\n\n\nfunc suite_name() -> String:\n\treturn _suite_name\n\n\nfunc failure_report() -> String:\n\tvar html_report := \"\"\n\tfor report in get_test_reports():\n\t\thtml_report += convert_rtf_to_html(str(report))\n\treturn html_report\n\n\nfunc create_record(_report_dir :String) -> String:\n\treturn GdUnitHtmlPatterns.TABLE_RECORD_TESTCASE\\\n\t\t.replace(GdUnitHtmlPatterns.REPORT_STATE, report_state().to_lower())\\\n\t\t.replace(GdUnitHtmlPatterns.REPORT_STATE_LABEL, report_state())\\\n\t\t.replace(GdUnitHtmlPatterns.TESTCASE_NAME, name())\\\n\t\t.replace(GdUnitHtmlPatterns.SKIPPED_COUNT, str(skipped_count()))\\\n\t\t.replace(GdUnitHtmlPatterns.ORPHAN_COUNT, str(orphan_count()))\\\n\t\t.replace(GdUnitHtmlPatterns.DURATION, LocalTime.elapsed(_duration))\\\n\t\t.replace(GdUnitHtmlPatterns.FAILURE_REPORT, failure_report())\n\n\nfunc add_testcase_reports(reports: Array[GdUnitReport]) -> void:\n\t_failure_reports.append_array(reports)\n\n\nfunc set_testcase_counters(p_error_count: int, p_failure_count: int, p_orphan_count: int,\n\tp_is_skipped: bool, p_is_flaky: bool, p_duration: int) -> void:\n\t_error_count = p_error_count\n\t_failure_count = p_failure_count\n\t_orphan_count = p_orphan_count\n\t_skipped_count = p_is_skipped\n\t_flaky_count = p_is_flaky as int\n\t_duration = p_duration\n\n\nfunc get_test_reports() -> Array[GdUnitReport]:\n\treturn _failure_reports\n"
  },
  {
    "path": "addons/gdUnit4/src/report/GdUnitTestCaseReport.gd.uid",
    "content": "uid://b45b4x01p5piu\n"
  },
  {
    "path": "addons/gdUnit4/src/report/GdUnitTestSuiteReport.gd",
    "content": "class_name GdUnitTestSuiteReport\nextends GdUnitReportSummary\n\nvar _time_stamp: int\nvar _failure_reports: Array[GdUnitReport] = []\n\n\nfunc _init(p_resource_path: String, p_name: String, p_test_count: int) -> void:\n\t_resource_path = p_resource_path\n\t_name = p_name\n\t_test_count = p_test_count\n\t_time_stamp = Time.get_unix_time_from_system() as int\n\n\nfunc create_record(report_link :String) -> String:\n\treturn GdUnitHtmlPatterns.build(GdUnitHtmlPatterns.TABLE_RECORD_TESTSUITE, self, report_link)\n\n\nfunc output_path(report_dir :String) -> String:\n\treturn \"%s/test_suites/%s.%s.html\" % [report_dir, path().replace(\"/\", \".\"), name()]\n\n\nfunc failure_report() -> String:\n\tvar html_report := \"\"\n\tfor report in _failure_reports:\n\t\thtml_report += convert_rtf_to_html(str(report))\n\treturn html_report\n\n\nfunc test_suite_failure_report() -> String:\n\treturn GdUnitHtmlPatterns.TABLE_REPORT_TESTSUITE\\\n\t\t.replace(GdUnitHtmlPatterns.REPORT_STATE, report_state().to_lower())\\\n\t\t.replace(GdUnitHtmlPatterns.REPORT_STATE_LABEL, report_state())\\\n\t\t.replace(GdUnitHtmlPatterns.ORPHAN_COUNT, str(orphan_count()))\\\n\t\t.replace(GdUnitHtmlPatterns.DURATION, LocalTime.elapsed(_duration))\\\n\t\t.replace(GdUnitHtmlPatterns.FAILURE_REPORT, failure_report())\n\n\nfunc write(report_dir :String) -> String:\n\tvar template := GdUnitHtmlPatterns.load_template(\"res://addons/gdUnit4/src/report/template/suite_report.html\")\n\ttemplate = GdUnitHtmlPatterns.build(template, self, \"\")\n\n\tvar report_output_path := output_path(report_dir)\n\tvar test_report_table := PackedStringArray()\n\tif not _failure_reports.is_empty():\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\ttest_report_table.append(test_suite_failure_report())\n\tfor test_report: GdUnitTestCaseReport in _reports:\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\ttest_report_table.append(test_report.create_record(report_output_path))\n\n\ttemplate = template.replace(GdUnitHtmlPatterns.TABLE_BY_TESTCASES, \"\\n\".join(test_report_table))\n\n\tvar dir := report_output_path.get_base_dir()\n\tif not DirAccess.dir_exists_absolute(dir):\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tDirAccess.make_dir_recursive_absolute(dir)\n\tFileAccess.open(report_output_path, FileAccess.WRITE).store_string(template)\n\treturn report_output_path\n\n\nfunc set_duration(p_duration :int) -> void:\n\t_duration = p_duration\n\n\nfunc time_stamp() -> int:\n\treturn _time_stamp\n\n\nfunc duration() -> int:\n\treturn _duration\n\n\nfunc set_skipped(skipped :int) -> void:\n\t_skipped_count += skipped\n\n\nfunc set_orphans(orphans :int) -> void:\n\t_orphan_count = orphans\n\n\nfunc set_failed(count :int) -> void:\n\t_failure_count += count\n\n\nfunc set_reports(failure_reports :Array[GdUnitReport]) -> void:\n\t_failure_reports = failure_reports\n\n\nfunc add_or_create_test_report(test_report: GdUnitTestCaseReport) -> void:\n\t_reports.append(test_report)\n\n\nfunc update_testsuite_counters(p_error_count: int, p_failure_count: int, p_orphan_count: int,\n\tp_is_skipped: bool, p_is_flaky: bool, p_duration: int) -> void:\n\t_error_count += p_error_count\n\t_failure_count += p_failure_count\n\t_orphan_count += p_orphan_count\n\t_skipped_count += p_is_skipped as int\n\t_flaky_count += p_is_flaky as int\n\t_duration += p_duration\n\n\nfunc set_testcase_counters(test_name: String, p_error_count: int, p_failure_count: int, p_orphan_count: int,\n\tp_is_skipped: bool, p_is_flaky: bool, p_duration: int) -> void:\n\tif _reports.is_empty():\n\t\treturn\n\tvar test_report:GdUnitTestCaseReport = _reports.filter(func (report: GdUnitTestCaseReport) -> bool:\n\t\treturn report.name() == test_name\n\t\t).back()\n\tif test_report:\n\t\ttest_report.set_testcase_counters(p_error_count, p_failure_count, p_orphan_count, p_is_skipped, p_is_flaky, p_duration)\n\n\nfunc add_testcase_reports(test_name: String, reports: Array[GdUnitReport] ) -> void:\n\tif reports.is_empty():\n\t\treturn\n\t# we lookup to latest matching report because of flaky tests could be retry the tests\n\t# and resultis in multipe report entries with the same name\n\tvar test_report:GdUnitTestCaseReport = _reports.filter(func (report: GdUnitTestCaseReport) -> bool:\n\t\treturn report.name() == test_name\n\t\t).back()\n\tif test_report:\n\t\ttest_report.add_testcase_reports(reports)\n"
  },
  {
    "path": "addons/gdUnit4/src/report/GdUnitTestSuiteReport.gd.uid",
    "content": "uid://b0r3oa3uxu1b2\n"
  },
  {
    "path": "addons/gdUnit4/src/report/JUnitXmlReport.gd",
    "content": "# This class implements the JUnit XML file format\n# based checked https://github.com/windyroad/JUnit-Schema/blob/master/JUnit.xsd\nclass_name JUnitXmlReport\nextends RefCounted\n\nconst GdUnitTools := preload(\"res://addons/gdUnit4/src/core/GdUnitTools.gd\")\n\nconst ATTR_CLASSNAME := \"classname\"\nconst ATTR_ERRORS := \"errors\"\nconst ATTR_FAILURES := \"failures\"\nconst ATTR_HOST := \"hostname\"\nconst ATTR_ID := \"id\"\nconst ATTR_MESSAGE := \"message\"\nconst ATTR_NAME := \"name\"\nconst ATTR_PACKAGE := \"package\"\nconst ATTR_SKIPPED := \"skipped\"\nconst ATTR_FLAKY := \"flaky\"\nconst ATTR_TESTS := \"tests\"\nconst ATTR_TIME := \"time\"\nconst ATTR_TIMESTAMP := \"timestamp\"\nconst ATTR_TYPE := \"type\"\n\nconst HEADER := '<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\\n'\n\nvar _report_path :String\nvar _iteration :int\n\n\nfunc _init(path :String, iteration :int) -> void:\n\t_iteration = iteration\n\t_report_path = path\n\n\nfunc write(report :GdUnitReportSummary) -> String:\n\tvar result_file: String = \"%s/results.xml\" % _report_path\n\tvar file := FileAccess.open(result_file, FileAccess.WRITE)\n\tif file == null:\n\t\tpush_warning(\"Can't saving the result to '%s'\\n Error: %s\" % [result_file, error_string(FileAccess.get_open_error())])\n\tfile.store_string(build_junit_report(report))\n\treturn result_file\n\n\nfunc build_junit_report(report :GdUnitReportSummary) -> String:\n\tvar iso8601_datetime := Time.get_date_string_from_system()\n\tvar test_suites := XmlElement.new(\"testsuites\")\\\n\t\t.attribute(ATTR_ID, iso8601_datetime)\\\n\t\t.attribute(ATTR_NAME, \"report_%s\" % _iteration)\\\n\t\t.attribute(ATTR_TESTS, report.test_count())\\\n\t\t.attribute(ATTR_FAILURES, report.failure_count())\\\n\t\t.attribute(ATTR_SKIPPED, report.skipped_count())\\\n\t\t.attribute(ATTR_FLAKY, report.flaky_count())\\\n\t\t.attribute(ATTR_TIME, JUnitXmlReport.to_time(report.duration()))\\\n\t\t.add_childs(build_test_suites(report))\n\tvar as_string := test_suites.to_xml()\n\ttest_suites.dispose()\n\treturn HEADER + as_string\n\n\nfunc build_test_suites(summary :GdUnitReportSummary) -> Array:\n\tvar test_suites :Array[XmlElement] = []\n\tfor index in summary.get_reports().size():\n\t\tvar suite_report :GdUnitTestSuiteReport = summary.get_reports()[index]\n\t\tvar iso8601_datetime := Time.get_datetime_string_from_unix_time(suite_report.time_stamp())\n\t\ttest_suites.append(XmlElement.new(\"testsuite\")\\\n\t\t\t.attribute(ATTR_ID, index)\\\n\t\t\t.attribute(ATTR_NAME, suite_report.name())\\\n\t\t\t.attribute(ATTR_PACKAGE, suite_report.path())\\\n\t\t\t.attribute(ATTR_TIMESTAMP, iso8601_datetime)\\\n\t\t\t.attribute(ATTR_HOST, \"localhost\")\\\n\t\t\t.attribute(ATTR_TESTS, suite_report.test_count())\\\n\t\t\t.attribute(ATTR_FAILURES, suite_report.failure_count())\\\n\t\t\t.attribute(ATTR_ERRORS, suite_report.error_count())\\\n\t\t\t.attribute(ATTR_SKIPPED, suite_report.skipped_count())\\\n\t\t\t.attribute(ATTR_FLAKY, suite_report.flaky_count())\\\n\t\t\t.attribute(ATTR_TIME, JUnitXmlReport.to_time(suite_report.duration()))\\\n\t\t\t.add_childs(build_test_cases(suite_report)))\n\treturn test_suites\n\n\nfunc build_test_cases(suite_report :GdUnitTestSuiteReport) -> Array:\n\tvar test_cases :Array[XmlElement] = []\n\tfor index in suite_report.get_reports().size():\n\t\tvar report :GdUnitTestCaseReport = suite_report.get_reports()[index]\n\t\ttest_cases.append( XmlElement.new(\"testcase\")\\\n\t\t\t.attribute(ATTR_NAME, JUnitXmlReport.encode_xml(report.name()))\\\n\t\t\t.attribute(ATTR_CLASSNAME, report.suite_name())\\\n\t\t\t.attribute(ATTR_TIME, JUnitXmlReport.to_time(report.duration()))\\\n\t\t\t.add_childs(build_reports(report)))\n\treturn test_cases\n\n\nfunc build_reports(test_report: GdUnitTestCaseReport) -> Array:\n\tvar failure_reports :Array[XmlElement] = []\n\n\tfor report: GdUnitReport in test_report.get_test_reports():\n\t\tif report.is_failure():\n\t\t\tfailure_reports.append(XmlElement.new(\"failure\")\\\n\t\t\t\t.attribute(ATTR_MESSAGE, \"FAILED: %s:%d\" % [test_report._resource_path, report.line_number()])\\\n\t\t\t\t.attribute(ATTR_TYPE, JUnitXmlReport.to_type(report.type()))\\\n\t\t\t\t.text(convert_rtf_to_text(report.message())))\n\t\telif report.is_error():\n\t\t\tfailure_reports.append(XmlElement.new(\"error\")\\\n\t\t\t\t.attribute(ATTR_MESSAGE, \"ERROR: %s:%d\" % [test_report._resource_path, report.line_number()])\\\n\t\t\t\t.attribute(ATTR_TYPE, JUnitXmlReport.to_type(report.type()))\\\n\t\t\t\t.text(convert_rtf_to_text(report.message())))\n\t\telif report.is_skipped():\n\t\t\tfailure_reports.append(XmlElement.new(\"skipped\")\\\n\t\t\t\t.attribute(ATTR_MESSAGE, \"SKIPPED: %s:%d\" % [test_report._resource_path, report.line_number()])\\\n\t\t\t\t.text(convert_rtf_to_text(report.message())))\n\treturn failure_reports\n\n\nfunc convert_rtf_to_text(bbcode :String) -> String:\n\treturn GdUnitTools.richtext_normalize(bbcode)\n\n\nstatic func to_type(type :int) -> String:\n\tmatch type:\n\t\tGdUnitReport.SUCCESS:\n\t\t\treturn \"SUCCESS\"\n\t\tGdUnitReport.WARN:\n\t\t\treturn \"WARN\"\n\t\tGdUnitReport.FAILURE:\n\t\t\treturn \"FAILURE\"\n\t\tGdUnitReport.ORPHAN:\n\t\t\treturn \"ORPHAN\"\n\t\tGdUnitReport.TERMINATED:\n\t\t\treturn \"TERMINATED\"\n\t\tGdUnitReport.INTERUPTED:\n\t\t\treturn \"INTERUPTED\"\n\t\tGdUnitReport.ABORT:\n\t\t\treturn \"ABORT\"\n\treturn \"UNKNOWN\"\n\n\nstatic func to_time(duration :int) -> String:\n\treturn \"%4.03f\" % (duration / 1000.0)\n\n\nstatic func encode_xml(value :String) -> String:\n\treturn value.xml_escape(true)\n\n\n#static func to_ISO8601_datetime() -> String:\n\t#return \"%04d-%02d-%02dT%02d:%02d:%02d\" % [date[\"year\"], date[\"month\"], date[\"day\"],  date[\"hour\"], date[\"minute\"], date[\"second\"]]\n"
  },
  {
    "path": "addons/gdUnit4/src/report/JUnitXmlReport.gd.uid",
    "content": "uid://d3v5dkdb3pqm1\n"
  },
  {
    "path": "addons/gdUnit4/src/report/XmlElement.gd",
    "content": "class_name XmlElement\nextends RefCounted\n\nvar _name :String\n# Dictionary[String, String]\nvar _attributes :Dictionary = {}\nvar _childs :Array[XmlElement] = []\nvar _parent :XmlElement = null\nvar _text :String = \"\"\n\n\nfunc _init(name :String) -> void:\n\t_name = name\n\n\nfunc dispose() -> void:\n\tfor child in _childs:\n\t\tchild.dispose()\n\t_childs.clear()\n\t_attributes.clear()\n\t_parent = null\n\n\nfunc attribute(name :String, value :Variant) -> XmlElement:\n\t_attributes[name] = str(value)\n\treturn self\n\n\nfunc text(p_text :String) -> XmlElement:\n\t_text = p_text if p_text.ends_with(\"\\n\") else p_text + \"\\n\"\n\treturn self\n\n\nfunc add_child(child :XmlElement) -> XmlElement:\n\t_childs.append(child)\n\tchild._parent = self\n\treturn self\n\n\nfunc add_childs(childs :Array[XmlElement]) -> XmlElement:\n\tfor child in childs:\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tadd_child(child)\n\treturn self\n\n\nfunc indentation() -> String:\n\treturn \"\" if _parent == null else _parent.indentation() + \"\t\"\n\n\nfunc to_xml() -> String:\n\tvar attributes := \"\"\n\tfor key in _attributes.keys() as Array[String]:\n\t\tattributes += ' {attr}=\"{value}\"'.format({\"attr\": key, \"value\": _attributes.get(key)})\n\n\tvar childs := \"\"\n\tfor child in _childs:\n\t\tchilds += child.to_xml()\n\n\treturn \"{_indentation}<{name}{attributes}>\\n{childs}{text}{_indentation}</{name}>\\n\"\\\n\t\t.format({\"name\": _name,\n\t\t\t\"attributes\": attributes,\n\t\t\t\"childs\": childs,\n\t\t\t\"_indentation\": indentation(),\n\t\t\t\"text\": cdata(_text)})\n\n\nfunc cdata(p_text :String) -> String:\n\treturn \"\" if p_text.is_empty() else \"<![CDATA[\\n{text}]]>\\n\".format({\"text\" : p_text})\n"
  },
  {
    "path": "addons/gdUnit4/src/report/XmlElement.gd.uid",
    "content": "uid://dpw2ousnkkibx\n"
  },
  {
    "path": "addons/gdUnit4/src/report/template/css/breadcrumb.css",
    "content": ".breadcrumb {\n\tdisplay: flex;\n\tborder-radius: 6px;\n\toverflow: hidden;\n\theight: 45px;\n\tz-index: 1;\n\tbackground-color: #9d73eb;\n\tmargin-top: 0px;\n\tmargin-bottom: 10px;\n\tbox-shadow: 0 0 3px black;\n}\n\n.breadcrumb a {\n\tposition: relative;\n\tdisplay: flex;\n\t-ms-flex-positive: 1;\n\tflex-grow: 1;\n\ttext-decoration: none;\n\tmargin: auto;\n\theight: 100%;\n\tcolor: white;\n}\n\n.breadcrumb a:first-child {\n\tpadding-left: 5.2px;\n}\n\n.breadcrumb a:last-child {\n\tpadding-right: 5.2px;\n}\n\n.breadcrumb a:after {\n\tcontent: \"\";\n\tposition: absolute;\n\tdisplay: inline-block;\n\twidth: 45px;\n\theight: 45px;\n\ttop: 0;\n\tright: -20px;\n\tbackground-color: #9d73eb;\n\tborder-top-right-radius: 5px;\n\ttransform: scale(0.707) rotate(45deg);\n\tbox-shadow: 2px -2px rgba(0, 0, 0, 0.25);\n\tz-index: 1;\n}\n\n.breadcrumb a:last-child:after {\n\tcontent: none;\n}\n\n.breadcrumb a.active,\n.breadcrumb a:hover {\n\tbackground: #b899f2;\n\tcolor: white;\n\ttext-decoration: underline;\n}\n\n.breadcrumb a.active:after,\n.breadcrumb a:hover:after {\n\tbackground: #b899f2;\n}\n\n.breadcrumb span {\n\tmargin: inherit;\n\tz-index: 2;\n}\n"
  },
  {
    "path": "addons/gdUnit4/src/report/template/css/logo.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://2n0cbwv2t23g\"\npath=\"res://.godot/imported/logo.png-37f10c549c1299049c5a6012ef6f9868.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/gdUnit4/src/report/template/css/logo.png\"\ndest_files=[\"res://.godot/imported/logo.png-37f10c549c1299049c5a6012ef6f9868.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/gdUnit4/src/report/template/css/styles.css",
    "content": "html,\nbody {\n\tdisplay: flex;\n\tflex-direction: column;\n\tmargin: 0;\n\tpadding: 0;\n\tfont-family: sans-serif;\n\tbackground-color: white;\n\theight: 100%;\n}\n\nmain {\n\tflex-grow: 1;\n\toverflow: auto;\n\tmargin: 0 10em;\n}\n\n\nheader {\n\tcolor: white;\n\tpadding: 1px;\n\tposition: relative;\n\tbackground-image: linear-gradient(to bottom right, #8058e3, #9d73eb);\n}\n\n.logo {\n\tposition: fixed;\n\ttop: 20px;\n\tleft: 20px;\n\tdisplay: flex;\n\talign-items: center;\n\tz-index: 1000;\n\tfilter: grayscale(1);\n\tmix-blend-mode: plus-lighter;\n}\n\n.logo img {\n\twidth: 64px;\n\theight: 64px;\n}\n\n.logo span {\n\tfont-size: 1.2em;\n\tcolor: lightslategray;\n}\n\n.report-container {\n\tmargin: 0 15em;\n\ttext-align: center;\n\tmargin-top: 60px;\n\tflex-grow: 0;\n}\n\nh1 {\n\tmargin: 0 0 20px 0;\n\tfont-size: 2.5em;\n\tfont-weight: normal;\n}\n\n.summary {\n\tdisplay: inline-flex;\n\tjustify-content: center;\n\tflex-wrap: nowrap;\n\tmargin-bottom: 20px;\n\talign-items: baseline;\n\tmax-width: 960px;\n}\n\n.summary-item {\n\tflex: 1;\n\tmin-width: 80px;\n}\n\n.label {\n\tfont-size: 1em;\n\tflex-wrap: nowrap;\n}\n\n.value {\n\tfont-size: 0.9em;\n\tdisplay: block;\n\tpadding-top: 10px;\n\tcolor: lightgray;\n}\n\n.success-rate {\n\tpadding-left: 40px;\n\tdisplay: flex;\n\tflex-direction: column;\n\tjustify-content: center;\n\talign-items: center;\n}\n\n.check-icon {\n\tbackground-color: #34c538;\n\tcolor: white;\n\twidth: 48px;\n\theight: 48px;\n\tborder-radius: 50%;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tfont-size: 1.4em;\n}\n\n.rate-text {\n\ttext-align: center;\n\tflex-wrap: nowrap;\n}\n\n.percentage {\n\tfont-size: 1.2em;\n\tfont-weight: bold;\n}\n\n\nnav {\n\tpadding: 20px 0px;\n\tfont-family: monospace;\n}\n\nnav ul {\n\tlist-style-type: none;\n\tpadding: 0;\n\tmargin: 0;\n\tdisplay: flex;\n\tjustify-content: flex-start;\n\tborder-bottom: 1px solid lightgray;\n}\n\nnav li {\n\tcursor: pointer;\n\tpadding: 5px 20px;\n\tfont-size: 1.1em;\n\tcolor: lightslategray;\n}\n\nnav li.active {\n\tcolor: darkslategray;\n\tborder-bottom: 1px solid darkslategray;\n\tfont-weight: bold;\n}\n\ndiv#content {\n\theight: calc(100vh - 400px);\n}\n\n\ntable {\n\twidth: 100%;\n\theight: 100%;\n\tborder-collapse: collapse;\n\toverflow: hidden;\n}\n\nthead th {\n\tposition: sticky;\n\ttop: 0;\n\tbackground-color: white;\n\tz-index: 1;\n\tborder-bottom: 2px solid #ddd;\n}\n\ntbody {\n\tdisplay: block;\n\t/* Limit the height of the table body */\n\tmax-height: calc(100vh - 400px);\n\t/* Enable scrolling on the table body */\n\toverflow-y: auto;\n}\n\nthead,\ntbody tr {\n\tdisplay: table;\n\twidth: 100%;\n\ttable-layout: fixed;\n}\n\ntbody td {\n\toverflow: hidden;\n}\n\n/* Ensure scrollbar visibility */\ntbody::-webkit-scrollbar {\n\theight: 4px;\n\twidth: 14px;\n}\n\ntbody::-webkit-scrollbar-thumb {\n\tbackground-color: #aaa6a6;\n\tborder-radius: 4px;\n}\n\ntbody::-webkit-scrollbar-track {\n\tbackground-color: #f1f1f1;\n}\n\nth,\ntd {\n\tfont-size: .9em;\n\tpadding: 5px 0px;\n\tborder-bottom: 1px solid #eee;\n\tcolor: lightslategrey;\n\ttext-align: left;\n\ttext-wrap: nowrap;\n\t/* Default max and min width for all columns */\n\tmax-width: 150px;\n\tmin-width: 80px;\n\twidth: 80px;\n}\n\nth {\n\tfont-size: 1em;\n\tfont-weight: normal;\n\tpadding-top: 20px;\n\tcolor: gray;\n\ttext-wrap: nowrap;\n}\n\n.tab-report {\n\tdisplay: grid;\n\tgrid-template-columns: 100%;\n\tmargin-bottom: 20px;\n}\n\n.tab-report-grid {\n\tdisplay: grid;\n\tgrid-template-columns: 70% 30%;\n\tmargin-bottom: 20px;\n}\n\n\n/* Specific styling for the first column (Testcase) */\nth:first-child,\ntd:first-child {\n\tpadding-left: 5px;\n\ttext-align: left;\n\t/* Max width for the first column */\n\tmin-width: 249px;\n\twidth: 250px;\n\t/* Enable scrollbar if content exceeds max-width */\n\twhite-space: nowrap;\n\toverflow: auto;\n}\n\n/* Scrollbar styles for first column */\ntd:first-child {\n\toverflow-x: auto;\n\ttext-overflow: initial;\n}\n\n/* Scrollbar appearance */\ntd:first-child::-webkit-scrollbar {\n\theight: 6px;\n}\n\ntd:first-child::-webkit-scrollbar-thumb {\n\tbackground-color: #888;\n\tborder-radius: 10px;\n}\n\ntd:first-child::-webkit-scrollbar-track {\n\tbackground-color: #f1f1f1;\n}\n\n/* Max width for Result column */\nth:nth-child(2),\ntd:nth-child(2) {\n\tmax-width: 140px;\n\tmin-width: 140px;\n\twidth: 140px;\n}\n\n/* Max width for Quick Results column */\nth:nth-child(9),\ntd:nth-child(9) {\n\tmax-width: 140px;\n\tmin-width: 140px;\n\twidth: 140px;\n\tpadding-right: 10px;\n}\n\n/* Background color for alternating groups */\n.group-bg-1 {\n\tbackground-color: #f1f1f1;\n}\n\n.group-bg-2 {\n\tbackground-color: #e0e0e0;\n}\n\n.grid-item {\n\toverflow: auto;\n\tpadding-left: 20px;\n\tcolor: lightslategrey;\n\tmax-height: calc(100vh - 350px);\n}\n\ndiv.tab td.report-column,\nth.report-column {\n\tdisplay: none;\n}\n\n/* Result status styles */\n.status {\n\tpadding: 2px 40px;\n\tborder-radius: 6px;\n\tcolor: black;\n\twidth: 40px;\n\tdisplay: flex;\n\talign-content: center;\n\talign-items: center;\n}\n\n.status-bar {\n\tdisplay: flex;\n\tborder-radius: 8px;\n\toverflow: hidden;\n\theight: 20px;\n\tflex-wrap: nowrap;\n\tjustify-content: space-evenly;\n}\n\n.status-bar-column {\n\tmargin: -2px;\n\tcolor: black;\n\tdisplay: flex;\n\talign-content: center;\n\talign-items: center;\n\ttransition: width 0.3s ease;\n}\n\n.status-skipped {\n\tbackground-color: #888888;\n}\n\n.status-passed {\n\tbackground-color: #63bb38;\n}\n\n.status-error {\n\tbackground-color: #fd1100;\n}\n\n.status-failed {\n\tbackground-color: #ed594f;\n}\n\n.status-flaky {\n\tbackground-color: #1d9a1f;\n}\n\n.status-warning {\n\tbackground-color: #fdda3f;\n}\n\ndiv.tab tr:hover {\n\tbackground-color: #d9e7fa;\n\tbox-shadow: 0 0 5px black;\n}\n\ndiv.tab tr.selected {\n\tbackground-color: #d9e7fa;\n}\n\ndiv.report-column {\n\tmargin-top: 10px;\n\twidth: 100%;\n\ttext-align: left;\n}\n\n.logging-container {\n\twidth: 100%;\n\theight: 100%;\n}\n\ndiv.godot-report-frame {\n\tmargin: 10px;\n\tfont-family: monospace;\n\theight: 100%;\n\tbackground-color: #eee;\n}\n\ndiv.include-footer {\n\tposition: fixed;\n\tbottom: 0;\n\twidth: 100%;\n\tdisplay: flex;\n}\n\nfooter {\n\tposition: static;\n\tleft: 0;\n\tbottom: 0;\n\twidth: 100%;\n\twhite-space: nowrap;\n\tcolor: lightgray;\n\tfont-size: 12px;\n\tbackground-image: linear-gradient(to bottom right, #8058e3, #9d73eb);\n\tdisplay: flex;\n\tjustify-content: space-between;\n\talign-items: center;\n}\n\nfooter p {\n\tpadding-left: 10em;\n}\n\nfooter .status-legend {\n\tdisplay: flex;\n\tgap: 15px;\n\twidth: 500px;\n}\n\nfooter a {\n\tcolor: lightgray;\n}\n\nfooter a:hover {\n\tcolor: whitesmoke;\n}\n\nfooter a:visited {\n\tcolor: whitesmoke;\n}\n\n.status-legend-item {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 5px;\n}\n\n.status-box {\n\twidth: 15px;\n\theight: 15px;\n\tborder-radius: 3px;\n\tdisplay: inline-block;\n}\n\n/* Normal link */\na {\n\tcolor: lightslategrey;\n}\n\n/* Link when hovered */\na:hover {\n\tcolor: #9d73eb;\n}\n\n/* Visited link */\na:visited {\n\tcolor: #8058e3;\n}\n\n/* Active link (while being clicked) */\na:active {\n\tcolor: #8058e3;\n\t/* Custom color when link is clicked */\n}\n\n\n@media (max-width: 1024px) {\n\t.summary {\n\t\tflex-direction: column;\n\t}\n\n\tnav ul {\n\t\tflex-wrap: wrap;\n\t}\n\n\tnav li {\n\t\tmargin-right: 10px;\n\t\tmargin-bottom: 5px;\n\t}\n}\n"
  },
  {
    "path": "addons/gdUnit4/src/report/template/folder_report.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n\t<meta charset=\"UTF-8\">\n\t<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n\t<meta http-equiv=\"x-ua-compatible\" content=\"IE=edge\" />\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n\t<title>GdUnit4 Testsuite</title>\n\t<link rel=\"stylesheet\" href=\"../css/styles.css\">\n\t<link href=\"../css/breadcrumb.css\" rel=\"stylesheet\" type=\"text/css\" />\n</head>\n\n<body>\n\t<header>\n\t\t<div class=\"logo\">\n\t\t\t<img src=\"../css/logo.png\" alt=\"GdUnit4 Logo\">\n\t\t\t<span>GdUnit4</span>\n\t\t</div>\n\t\t<div class=\"report-container\">\n\t\t\t<h1>Report by Paths</h1>\n\t\t\t<div>\n\t\t\t\t<span class=\"label\">${resource_path}</span>\n\t\t\t</div>\n\t\t\t<div class=\"summary\">\n\t\t\t\t<div class=\"summary-item\">\n\t\t\t\t\t<span class=\"label\">TestSuites</span>\n\t\t\t\t\t<span class=\"value\">${suite_count}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"summary-item\">\n\t\t\t\t\t<span class=\"label\">Tests</span>\n\t\t\t\t\t<span class=\"value\">${test_count}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"summary-item\">\n\t\t\t\t\t<span class=\"label\">Skipped</span>\n\t\t\t\t\t<span class=\"value\">${skipped_count}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"summary-item\">\n\t\t\t\t\t<span class=\"label\">Flaky</span>\n\t\t\t\t\t<span class=\"value\">${flaky_count}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"summary-item\">\n\t\t\t\t\t<span class=\"label\">Failures</span>\n\t\t\t\t\t<span class=\"value\">${failure_count}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"summary-item\">\n\t\t\t\t\t<span class=\"label\">Orphans</span>\n\t\t\t\t\t<span class=\"value\">${orphan_count}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"summary-item\">\n\t\t\t\t\t<span class=\"label\">Duration</span>\n\t\t\t\t\t<span class=\"value\">${duration}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"success-rate\">\n\t\t\t\t\t<div class=\"check-icon status-${report_state}\">✓</div>\n\t\t\t\t\t<div class=\"rate-text\">\n\t\t\t\t\t\t<span class=\"label\">Success Rate</span>\n\t\t\t\t\t\t<span class=\"value\">${success_percent}</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t\t<div class=\"breadcrumb\">\n\t\t\t<a href=\"../index.html\"><span>All</span></a>\n\t\t\t<a href=\"${breadcrumb_path_link}\"><span>${path}</span></a>\n\t\t\t<a class=\"active\" href=\"#\"><span>${testsuite_name}</span></a>\n\t\t</div>\n\t</header>\n\n\t<main>\n\t\t<div>\n\t\t\t<div class=\"tab-report\">\n\t\t\t\t<div class=\"grid-item tab\">\n\t\t\t\t\t<table id=\"report-table\">\n\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<th>TestSuites</th>\n\t\t\t\t\t\t\t\t<th>Result</th>\n\t\t\t\t\t\t\t\t<th>Tests</th>\n\t\t\t\t\t\t\t\t<th>Skipped</th>\n\t\t\t\t\t\t\t\t<th>Flaky</th>\n\t\t\t\t\t\t\t\t<th>Failures</th>\n\t\t\t\t\t\t\t\t<th>Orphans</th>\n\t\t\t\t\t\t\t\t<th>Duration</th>\n\t\t\t\t\t\t\t\t<th>Success rate</th>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t${report_table_testsuites}\n\t\t\t\t\t\t</tbody>\n\t\t\t\t\t</table>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</main>\n\n\t<footer>\n\t\t<p>Generated by <a href=\"https://github.com/MikeSchulze/gdUnit4\">GdUnit4</a> at ${buid_date}</p>\n\t\t<div class=\"status-legend\">\n\t\t\t<span class=\"status-legend-item\">\n\t\t\t\t<span class=\"status-box status-skipped\"></span> Skipped\n\t\t\t</span>\n\t\t\t<span class=\"status-legend-item\">\n\t\t\t\t<span class=\"status-box status-passed\"></span> Passed\n\t\t\t</span>\n\t\t\t<span class=\"status-legend-item\">\n\t\t\t\t<span class=\"status-box status-flaky\"></span> Flaky\n\t\t\t</span>\n\t\t\t<span class=\"status-legend-item\">\n\t\t\t\t<span class=\"status-box status-warning\"></span> Warning\n\t\t\t</span>\n\t\t\t<span class=\"status-legend-item\">\n\t\t\t\t<span class=\"status-box status-failed\"></span> Failed\n\t\t\t</span>\n\t\t\t<span class=\"status-legend-item\">\n\t\t\t\t<span class=\"status-box status-error\"></span> Error\n\t\t\t</span>\n\t\t</div>\n\t</footer>\n</body>\n\n</html>\n"
  },
  {
    "path": "addons/gdUnit4/src/report/template/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n\t<meta charset=\"UTF-8\">\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n\t<title>GdUnit4 Report</title>\n\t<link rel=\"stylesheet\" href=\"css/styles.css\">\n</head>\n\n<body>\n\t<header>\n\t\t<div class=\"logo\">\n\t\t\t<img src=\"css/logo.png\" alt=\"GdUnit4 Logo\">\n\t\t\t<span>GdUnit4</span>\n\t\t</div>\n\t\t<div class=\"report-container\">\n\t\t\t<h1>Summary Report</h1>\n\t\t\t<div class=\"summary\">\n\t\t\t\t<div class=\"summary-item\">\n\t\t\t\t\t<span class=\"label\">Test Suites</span>\n\t\t\t\t\t<span class=\"value\">${suite_count}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"summary-item\">\n\t\t\t\t\t<span class=\"label\">Tests</span>\n\t\t\t\t\t<span class=\"value\">${test_count}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"summary-item\">\n\t\t\t\t\t<span class=\"label\">Skipped</span>\n\t\t\t\t\t<span class=\"value\">${skipped_count}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"summary-item\">\n\t\t\t\t\t<span class=\"label\">Flaky</span>\n\t\t\t\t\t<span class=\"value\">${flaky_count}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"summary-item\">\n\t\t\t\t\t<span class=\"label\">Failures</span>\n\t\t\t\t\t<span class=\"value\">${failure_count}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"summary-item\">\n\t\t\t\t\t<span class=\"label\">Orphans</span>\n\t\t\t\t\t<span class=\"value\">${orphan_count}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"summary-item\">\n\t\t\t\t\t<span class=\"label\">Duration</span>\n\t\t\t\t\t<span class=\"value\">${duration}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"success-rate\">\n\t\t\t\t\t<div class=\"check-icon status-${report_state}\">✓</div>\n\t\t\t\t\t<div class=\"rate-text\">\n\t\t\t\t\t\t<span class=\"label\">Success Rate</span>\n\t\t\t\t\t\t<span class=\"value\">${success_percent}</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</header>\n\t<main>\n\t\t<nav>\n\t\t\t<ul>\n\t\t\t\t<li class=\"active\" data-page=\"test-suites\">TEST SUITES</li>\n\t\t\t\t<li data-page=\"paths\">PATHS</li>\n\t\t\t\t<li data-page=\"logging\">LOGGING</li>\n\t\t\t</ul>\n\t\t</nav>\n\t\t<div id=\"content\">\n\t\t\t<!-- Content will be loaded here based on selected page -->\n\t\t</div>\n\t</main>\n\n\t<footer>\n\t\t<p>Generated by <a href=\"https://github.com/MikeSchulze/gdUnit4\">GdUnit4</a> at ${buid_date}</p>\n\t\t<div class=\"status-legend\">\n\t\t\t<span class=\"status-legend-item\">\n\t\t\t\t<span class=\"status-box status-skipped\"></span> Skipped\n\t\t\t</span>\n\t\t\t<span class=\"status-legend-item\">\n\t\t\t\t<span class=\"status-box status-passed\"></span> Passed\n\t\t\t</span>\n\t\t\t<span class=\"status-legend-item\">\n\t\t\t\t<span class=\"status-box status-flaky\"></span> Flaky\n\t\t\t</span>\n\t\t\t<span class=\"status-legend-item\">\n\t\t\t\t<span class=\"status-box status-warning\"></span> Warning\n\t\t\t</span>\n\t\t\t<span class=\"status-legend-item\">\n\t\t\t\t<span class=\"status-box status-failed\"></span> Failed\n\t\t\t</span>\n\t\t\t<span class=\"status-legend-item\">\n\t\t\t\t<span class=\"status-box status-error\"></span> Error\n\t\t\t</span>\n\t\t</div>\n\t</footer>\n\n\t<script>\n\t\t// Simple JavaScript to handle page switching\n\t\tdocument.querySelectorAll('nav li').forEach(item => {\n\t\t\titem.addEventListener('click', function () {\n\t\t\t\tdocument.querySelectorAll('nav li').forEach(li => li.classList.remove('active'));\n\t\t\t\tthis.classList.add('active');\n\t\t\t\tloadPage(this.getAttribute('data-page'));\n\t\t\t});\n\t\t});\n\n\t\tfunction loadPage(page) {\n\t\t\tif (page === 'test-suites') {\n\t\t\t\tdocument.getElementById('content').innerHTML = `\n\t\t\t\t\t<div class=\"grid-item tab\">\n\t\t\t\t\t\t<table>\n\t\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t\t<th>Test Suites</th>\n\t\t\t\t\t\t\t\t\t<th>State</th>\n\t\t\t\t\t\t\t\t\t<th>Tests</th>\n\t\t\t\t\t\t\t\t\t<th>Skipped</th>\n\t\t\t\t\t\t\t\t\t<th>Flaky</th>\n\t\t\t\t\t\t\t\t\t<th>Failures</th>\n\t\t\t\t\t\t\t\t\t<th>Orphans</th>\n\t\t\t\t\t\t\t\t\t<th>Duration</th>\n\t\t\t\t\t\t\t\t\t<th>Quick State</th>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t${report_table_testsuites}\n\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t</table>\n\t\t\t\t\t</div>`\n\t\t\t} else if (page === 'paths') {\n\t\t\t\tdocument.getElementById('content').innerHTML = `\n\t\t\t\t\t<div class=\"grid-item tab\">\n\t\t\t\t\t\t<table>\n\t\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t\t<th>Paths</th>\n\t\t\t\t\t\t\t\t\t<th>State</th>\n\t\t\t\t\t\t\t\t\t<th>Tests</th>\n\t\t\t\t\t\t\t\t\t<th>Skipped</th>\n\t\t\t\t\t\t\t\t\t<th>Flaky</th>\n\t\t\t\t\t\t\t\t\t<th>Failures</th>\n\t\t\t\t\t\t\t\t\t<th>Orphans</th>\n\t\t\t\t\t\t\t\t\t<th>Duration</th>\n\t\t\t\t\t\t\t\t\t<th>Quick State</th>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t${report_table_paths}\n\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t</table>\n\t\t\t\t\t</div>`\n\t\t\t} else if (page === 'logging') {\n\t\t\t\tdocument.getElementById('content').innerHTML = `\n\t\t\t\t\t<h3>${godot_log_file}</h3>\n\t\t\t\t\t<div class=\"logging-container\">\n\t\t\t\t\t\t<iframe id=\"logging_content\" src=\"${log_report}\"  frameborder=\"0\" height=\"100%\" width=\"100%\"></iframe>\n\t\t\t\t\t</div>`\n\t\t\t}\n\t\t}\n\n\t\t// Load default page\n\t\tloadPage('test-suites');\n\t</script>\n</body>\n\n</html>\n"
  },
  {
    "path": "addons/gdUnit4/src/report/template/suite_report.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n\t<meta charset=\"UTF-8\">\n\t<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n\t<meta http-equiv=\"x-ua-compatible\" content=\"IE=edge\" />\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n\t<title>GdUnit4 Testsuite</title>\n\t<link rel=\"stylesheet\" href=\"../css/styles.css\">\n\t<link href=\"../css/breadcrumb.css\" rel=\"stylesheet\" type=\"text/css\" />\n\n\t<script src=\"https://code.jquery.com/jquery-3.6.4.js\"></script>\n\t<script>\n\t\t$(document).ready(function () {\n\t\t\tvar report_view = $('#report_area').find('.report-column')\n\n\t\t\t$('#report-table tr').click(function () {\n\t\t\t\tvar report = $(this).find('.report-column').html();\n\t\t\t\treport_view.html(report)\n\t\t\t\t$('.selected').removeClass('selected');\n\t\t\t\t$(this).addClass('selected');\n\t\t\t});\n\t\t});\n\t</script>\n</head>\n\n<body>\n\t<header>\n\t\t<div class=\"logo\">\n\t\t\t<img src=\"../css/logo.png\" alt=\"GdUnit4 Logo\">\n\t\t\t<span>GdUnit4</span>\n\t\t</div>\n\t\t<div class=\"report-container\">\n\t\t\t<h1>Testsuite Report</h1>\n\t\t\t<div>\n\t\t\t\t<span class=\"label\">${resource_path}</span>\n\t\t\t</div>\n\t\t\t<div class=\"summary\">\n\t\t\t\t<div class=\"summary-item\">\n\t\t\t\t\t<span class=\"label\">Tests</span>\n\t\t\t\t\t<span class=\"value\">${test_count}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"summary-item\">\n\t\t\t\t\t<span class=\"label\">Skipped</span>\n\t\t\t\t\t<span class=\"value\">${skipped_count}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"summary-item\">\n\t\t\t\t\t<span class=\"label\">Flaky</span>\n\t\t\t\t\t<span class=\"value\">${flaky_count}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"summary-item\">\n\t\t\t\t\t<span class=\"label\">Failures</span>\n\t\t\t\t\t<span class=\"value\">${failure_count}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"summary-item\">\n\t\t\t\t\t<span class=\"label\">Orphans</span>\n\t\t\t\t\t<span class=\"value\">${orphan_count}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"summary-item\">\n\t\t\t\t\t<span class=\"label\">Duration</span>\n\t\t\t\t\t<span class=\"value\">${duration}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"success-rate\">\n\t\t\t\t\t<div class=\"check-icon status-${report_state}\">✓</div>\n\t\t\t\t\t<div class=\"rate-text\">\n\t\t\t\t\t\t<span class=\"label\">Success Rate</span>\n\t\t\t\t\t\t<span class=\"value\">${success_percent}</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t\t<div class=\"breadcrumb\">\n\t\t\t<a href=\"../index.html\"><span>All</span></a>\n\t\t\t<a href=\"${breadcrumb_path_link}\"><span>${path}</span></a>\n\t\t\t<a class=\"active\" href=\"#\"><span>${testsuite_name}</span></a>\n\t\t</div>\n\t</header>\n\n\t<main>\n\t\t<div class=\"tab-report-grid\">\n\t\t\t<div class=\"grid-item tab\">\n\t\t\t\t<table id=\"report-table\">\n\t\t\t\t\t<thead>\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<th>Testcase</th>\n\t\t\t\t\t\t\t<th>Result</th>\n\t\t\t\t\t\t\t<th>Skipped</th>\n\t\t\t\t\t\t\t<th>Orphans</th>\n\t\t\t\t\t\t\t<th>Duration</th>\n\t\t\t\t\t\t\t<th class=\"report-column\">Report</th>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t</thead>\n\t\t\t\t\t<tbody>\n\t\t\t\t\t\t${report_table_tests}\n\t\t\t\t\t</tbody>\n\t\t\t\t</table>\n\t\t\t</div>\n\t\t\t<div id=\"report_area\" class=\"grid-item tab\">\n\t\t\t\t<h4>Failure Report</h4>\n\t\t\t\t<div class=\"report-column\"></div>\n\t\t\t</div>\n\t\t</div>\n\t</main>\n\n\t<footer>\n\t\t<p>Generated by <a href=\"https://github.com/MikeSchulze/gdUnit4\">GdUnit4</a> at ${buid_date}</p>\n\t\t<div class=\"status-legend\">\n\t\t\t<span class=\"status-legend-item\">\n\t\t\t\t<span class=\"status-box status-skipped\"></span> Skipped\n\t\t\t</span>\n\t\t\t<span class=\"status-legend-item\">\n\t\t\t\t<span class=\"status-box status-passed\"></span> Passed\n\t\t\t</span>\n\t\t\t<span class=\"status-legend-item\">\n\t\t\t\t<span class=\"status-box status-flaky\"></span> Flaky\n\t\t\t</span>\n\t\t\t<span class=\"status-legend-item\">\n\t\t\t\t<span class=\"status-box status-warning\"></span> Warning\n\t\t\t</span>\n\t\t\t<span class=\"status-legend-item\">\n\t\t\t\t<span class=\"status-box status-failed\"></span> Failed\n\t\t\t</span>\n\t\t\t<span class=\"status-legend-item\">\n\t\t\t\t<span class=\"status-box status-error\"></span> Error\n\t\t\t</span>\n\t\t</div>\n\t</footer>\n</body>\n\n<script>\n\tfunction groupTableRows() {\n\t\tconst table = document.getElementById('report-table');\n\t\tconst rows = table.querySelectorAll('tbody tr');\n\n\t\tlet previousTestCase = '';\n\t\tlet groupStartIndex = 0;\n\t\tlet groupColorToggle = true; // Toggle between two colors\n\n\t\trows.forEach((row, index) => {\n\t\t\tconst testCaseName = row.cells[0].textContent.trim();\n\n\t\t\tif (testCaseName !== previousTestCase) {\n\t\t\t\t// Close the previous group\n\t\t\t\tif (index > 0 && groupStartIndex !== index - 1) {\n\t\t\t\t\t// Apply background color to the group\n\t\t\t\t\tconst groupClass = groupColorToggle ? 'group-bg-1' : 'group-bg-2';\n\t\t\t\t\tfor (let i = groupStartIndex; i <= index - 1; i++) {\n\t\t\t\t\t\trows[i].classList.add(groupClass);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Toggle background color for the next group\n\t\t\t\t\tgroupColorToggle = !groupColorToggle;\n\t\t\t\t}\n\n\t\t\t\t// Start a new group\n\t\t\t\tgroupStartIndex = index;\n\t\t\t}\n\n\t\t\tpreviousTestCase = testCaseName;\n\t\t});\n\n\t\t// Handle the last group\n\t\tif (groupStartIndex !== rows.length - 1) {\n\t\t\t// Apply background color to the last group\n\t\t\tconst groupClass = groupColorToggle ? 'group-bg-1' : 'group-bg-2';\n\t\t\tfor (let i = groupStartIndex; i < rows.length; i++) {\n\t\t\t\trows[i].classList.add(groupClass);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Call the function to group table rows after the DOM loads\n\tdocument.addEventListener('DOMContentLoaded', groupTableRows);\n</script>\n\n</html>\n"
  },
  {
    "path": "addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd",
    "content": "class_name GdUnitSpyBuilder\nextends GdUnitClassDoubler\n\nconst GdUnitTools := preload(\"res://addons/gdUnit4/src/core/GdUnitTools.gd\")\nconst SPY_TEMPLATE :GDScript = preload(\"res://addons/gdUnit4/src/spy/GdUnitSpyImpl.gd\")\nconst EXCLUDE_PROPERTIES_TO_COPY = [\"script\", \"type\"]\n\n\nstatic func build(to_spy: Variant, debug_write := false) -> Variant:\n\tif GdObjects.is_singleton(to_spy):\n\t\t@warning_ignore(\"unsafe_cast\")\n\t\tpush_error(\"Spy on a Singleton is not allowed! '%s'\" % (to_spy as Object).get_class())\n\t\treturn null\n\n\t# if resource path load it before\n\tif GdObjects.is_scene_resource_path(to_spy):\n\t\tvar scene_resource_path :String = to_spy\n\t\tif not FileAccess.file_exists(scene_resource_path):\n\t\t\tpush_error(\"Can't build spy on scene '%s'! The given resource not exists!\" % scene_resource_path)\n\t\t\treturn null\n\t\tvar scene_to_spy: PackedScene = load(scene_resource_path)\n\t\treturn spy_on_scene(scene_to_spy.instantiate() as Node, debug_write)\n\t# spy checked PackedScene\n\tif GdObjects.is_scene(to_spy):\n\t\tvar scene_to_spy: PackedScene = to_spy\n\t\treturn spy_on_scene(scene_to_spy.instantiate() as Node, debug_write)\n\t# spy checked a scene instance\n\tif GdObjects.is_instance_scene(to_spy):\n\t\t@warning_ignore(\"unsafe_cast\")\n\t\treturn spy_on_scene(to_spy as Node, debug_write)\n\n\tvar excluded_functions := []\n\tif to_spy is Callable:\n\t\t@warning_ignore(\"unsafe_cast\")\n\t\tto_spy = CallableDoubler.new(to_spy as Callable)\n\t\texcluded_functions = CallableDoubler.excluded_functions()\n\n\tvar spy := spy_on_script(to_spy, excluded_functions, debug_write)\n\tif spy == null:\n\t\treturn null\n\tvar spy_instance :Object = spy.new()\n\t@warning_ignore(\"unsafe_cast\")\n\tcopy_properties(to_spy as Object, spy_instance)\n\t@warning_ignore(\"return_value_discarded\")\n\tGdUnitObjectInteractions.reset(spy_instance)\n\t@warning_ignore(\"unsafe_method_access\")\n\tspy_instance.__set_singleton(to_spy)\n\t# we do not call the original implementation for _ready and all input function, this is actualy done by the engine\n\t@warning_ignore(\"unsafe_method_access\")\n\tspy_instance.__exclude_method_call([ \"_input\", \"_gui_input\", \"_input_event\", \"_unhandled_input\"])\n\treturn register_auto_free(spy_instance)\n\n\nstatic func get_class_info(clazz :Variant) -> Dictionary:\n\tvar clazz_path := GdObjects.extract_class_path(clazz)\n\tvar clazz_name :String = GdObjects.extract_class_name(clazz).value()\n\treturn {\n\t\t\"class_name\" : clazz_name,\n\t\t\"class_path\" : clazz_path\n\t}\n\n\nstatic func spy_on_script(instance :Variant, function_excludes :PackedStringArray, debug_write :bool) -> GDScript:\n\tif GdArrayTools.is_array_type(instance):\n\t\tif GdUnitSettings.is_verbose_assert_errors():\n\t\t\tpush_error(\"Can't build spy checked type '%s'! Spy checked Container Built-In Type not supported!\" % type_string(typeof(instance)))\n\t\treturn null\n\tvar class_info := get_class_info(instance)\n\tvar clazz_name :String = class_info.get(\"class_name\")\n\tvar clazz_path :PackedStringArray = class_info.get(\"class_path\", [clazz_name])\n\tif not GdObjects.is_instance(instance):\n\t\tif GdUnitSettings.is_verbose_assert_errors():\n\t\t\tpush_error(\"Can't build spy for class type '%s'! Using an instance instead e.g. 'spy(<instance>)'\" % [clazz_name])\n\t\treturn null\n\t@warning_ignore(\"unsafe_cast\")\n\tvar lines := load_template(SPY_TEMPLATE.source_code, class_info, instance as Object)\n\t@warning_ignore(\"unsafe_cast\")\n\tlines += double_functions(instance as Object, clazz_name, clazz_path, GdUnitSpyFunctionDoubler.new(), function_excludes)\n\n\tvar spy := GDScript.new()\n\tspy.source_code = \"\\n\".join(lines)\n\tspy.resource_name = \"Spy%s.gd\" % clazz_name\n\tspy.resource_path = GdUnitFileAccess.create_temp_dir(\"spy\") + \"/Spy%s_%d.gd\" % [clazz_name, Time.get_ticks_msec()]\n\n\tif debug_write:\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tDirAccess.remove_absolute(spy.resource_path)\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tResourceSaver.save(spy, spy.resource_path)\n\tvar error := spy.reload(true)\n\tif error != OK:\n\t\tpush_error(\"Unexpected Error!, SpyBuilder error, please contact the developer.\")\n\t\treturn null\n\treturn spy\n\n\nstatic func spy_on_scene(scene :Node, debug_write :bool) -> Object:\n\tif scene.get_script() == null:\n\t\tif GdUnitSettings.is_verbose_assert_errors():\n\t\t\tpush_error(\"Can't create a spy checked a scene without script '%s'\" % scene.get_scene_file_path())\n\t\treturn null\n\t# buils spy checked original script\n\t@warning_ignore(\"unsafe_cast\")\n\tvar scene_script :Object = (scene.get_script() as GDScript).new()\n\tvar spy := spy_on_script(scene_script, GdUnitClassDoubler.EXLCUDE_SCENE_FUNCTIONS, debug_write)\n\tscene_script.free()\n\tif spy == null:\n\t\treturn null\n\t# replace original script whit spy\n\tscene.set_script(spy)\n\treturn register_auto_free(scene)\n\n\nstatic func copy_properties(source :Object, dest :Object) -> void:\n\tfor property in source.get_property_list():\n\t\tvar property_name :String = property[\"name\"]\n\t\tvar property_value :Variant = source.get(property_name)\n\t\tif EXCLUDE_PROPERTIES_TO_COPY.has(property_name):\n\t\t\tcontinue\n\t\t#if dest.get(property_name) == null:\n\t\t#\tprints(\"|%s|\" % property_name, source.get(property_name))\n\n\t\t# check for invalid name property\n\t\tif property_name == \"name\" and property_value == \"\":\n\t\t\tdest.set(property_name, \"<empty>\");\n\t\t\tcontinue\n\t\tdest.set(property_name, property_value)\n\n\nstatic func register_auto_free(obj :Variant) -> Variant:\n\treturn GdUnitThreadManager.get_current_context().get_execution_context().register_auto_free(obj)\n"
  },
  {
    "path": "addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd.uid",
    "content": "uid://clu5tawv2n2rt\n"
  },
  {
    "path": "addons/gdUnit4/src/spy/GdUnitSpyFunctionDoubler.gd",
    "content": "class_name GdUnitSpyFunctionDoubler\nextends GdFunctionDoubler\n\n\nconst TEMPLATE_RETURN_VARIANT = \"\"\"\n\tvar args__: Array = [\"$(func_name)\", $(arguments)]\n\n\tif $(instance)__is_verify_interactions():\n\t\t$(instance)__verify_interactions(args__)\n\t\treturn ${default_return_value}\n\telse:\n\t\t$(instance)__save_function_interaction(args__)\n\n\tif $(instance)__do_call_real_func(\"$(func_name)\"):\n\t\treturn $(await)super($(arguments))\n\treturn ${default_return_value}\n\n\"\"\"\n\n\nconst TEMPLATE_RETURN_VOID = \"\"\"\n\tvar args__: Array = [\"$(func_name)\", $(arguments)]\n\n\tif $(instance)__is_verify_interactions():\n\t\t$(instance)__verify_interactions(args__)\n\t\treturn\n\telse:\n\t\t$(instance)__save_function_interaction(args__)\n\n\tif $(instance)__do_call_real_func(\"$(func_name)\"):\n\t\t$(await)super($(arguments))\n\n\"\"\"\n\n\nconst TEMPLATE_RETURN_VOID_VARARG = \"\"\"\n\tvar varargs__: Array = __filter_vargs([$(varargs)])\n\tvar args__: Array = [\"$(func_name)\", $(arguments)] + varargs__\n\n\tif $(instance)__is_verify_interactions():\n\t\t$(instance)__verify_interactions(args__)\n\t\treturn\n\telse:\n\t\t$(instance)__save_function_interaction(args__)\n\n\t$(await)$(instance)__call_func(\"$(func_name)\", [$(arguments)] + varargs__)\n\n\"\"\"\n\n\nconst TEMPLATE_RETURN_VARIANT_VARARG = \"\"\"\n\tvar varargs__: Array = __filter_vargs([$(varargs)])\n\tvar args__: Array = [\"$(func_name)\", $(arguments)] + varargs__\n\n\tif $(instance)__is_verify_interactions():\n\t\t$(instance)__verify_interactions(args__)\n\t\treturn ${default_return_value}\n\telse:\n\t\t$(instance)__save_function_interaction(args__)\n\n\treturn $(await)$(instance)__call_func(\"$(func_name)\", [$(arguments)] + varargs__)\n\n\"\"\"\n\n\nconst TEMPLATE_CALLABLE_CALL = \"\"\"\n\tvar used_arguments__ := __filter_vargs([$(arguments)])\n\n\tif __is_verify_interactions():\n\t\t__verify_interactions([\"call\", used_arguments__])\n\t\treturn ${default_return_value}\n\telse:\n\t\t# append possible binded values to complete the original argument list\n\t\tvar args__ := used_arguments__.duplicate()\n\t\targs__.append_array(super.get_bound_arguments())\n\t\t__save_function_interaction([\"call\", args__])\n\n\tif __do_call_real_func(\"call\"):\n\t\treturn _cb.callv(used_arguments__)\n\treturn ${default_return_value}\n\n\"\"\"\n\n\nfunc _init(push_errors :bool = false) -> void:\n\tsuper._init(push_errors)\n\n\nfunc get_template(fd: GdFunctionDescriptor, is_callable: bool) -> String:\n\tif is_callable and  fd.name() == \"call\":\n\t\treturn TEMPLATE_CALLABLE_CALL\n\tif  fd.is_vararg():\n\t\treturn TEMPLATE_RETURN_VOID_VARARG if fd.return_type() == TYPE_NIL else TEMPLATE_RETURN_VARIANT_VARARG\n\tvar return_type :Variant = fd.return_type()\n\tif return_type is StringName:\n\t\treturn TEMPLATE_RETURN_VARIANT\n\treturn TEMPLATE_RETURN_VOID if (return_type == TYPE_NIL or return_type == GdObjects.TYPE_VOID) else TEMPLATE_RETURN_VARIANT\n"
  },
  {
    "path": "addons/gdUnit4/src/spy/GdUnitSpyFunctionDoubler.gd.uid",
    "content": "uid://d3mtv37x4xun\n"
  },
  {
    "path": "addons/gdUnit4/src/spy/GdUnitSpyImpl.gd",
    "content": "\nconst __INSTANCE_ID = \"${instance_id}\"\nconst __SOURCE_CLASS = \"${source_class}\"\n\nvar __instance_delegator :Object\nvar __excluded_methods :PackedStringArray = []\n\n\nstatic func __instance() -> Variant:\n\treturn Engine.get_meta(__INSTANCE_ID)\n\n\nfunc _notification(what :int) -> void:\n\tif what == NOTIFICATION_PREDELETE:\n\t\tif Engine.has_meta(__INSTANCE_ID):\n\t\t\tEngine.remove_meta(__INSTANCE_ID)\n\n\nfunc __instance_id() -> String:\n\treturn __INSTANCE_ID\n\n\nfunc __set_singleton(delegator :Object) -> void:\n\t# store self need to mock static functions\n\tEngine.set_meta(__INSTANCE_ID, self)\n\t__instance_delegator = delegator\n\n\nfunc __release_double() -> void:\n\t# we need to release the self reference manually to prevent orphan nodes\n\tEngine.remove_meta(__INSTANCE_ID)\n\t__instance_delegator = null\n\n\nfunc __do_call_real_func(func_name :String) -> bool:\n\treturn not __excluded_methods.has(func_name)\n\n\nfunc __exclude_method_call(exluded_methods :PackedStringArray) -> void:\n\t__excluded_methods.append_array(exluded_methods)\n\n\nfunc __call_func(func_name :String, arguments :Array) -> Variant:\n\treturn __instance_delegator.callv(func_name, arguments)\n"
  },
  {
    "path": "addons/gdUnit4/src/spy/GdUnitSpyImpl.gd.uid",
    "content": "uid://cph27u78vfdcb\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/GdUnitConsole.gd",
    "content": "@tool\nextends Control\n\nconst GdUnitUpdateClient = preload(\"res://addons/gdUnit4/src/update/GdUnitUpdateClient.gd\")\nconst TITLE = \"gdUnit4 ${version} Console\"\n\n@onready var header := $VBoxContainer/Header\n@onready var title: RichTextLabel = $VBoxContainer/Header/header_title\n@onready var output: RichTextLabel = $VBoxContainer/Console/TextEdit\n\nvar _text_color: Color\nvar _function_color: Color\nvar _engine_type_color: Color\nvar _statistics := {}\nvar _summary := {\n\t\"total_count\": 0,\n\t\"error_count\": 0,\n\t\"failed_count\": 0,\n\t\"skipped_count\": 0,\n\t\"flaky_count\": 0,\n\t\"orphan_nodes\": 0\n}\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc _ready() -> void:\n\tinit_colors()\n\tGdUnitFonts.init_fonts(output)\n\tGdUnit4Version.init_version_label(title)\n\tGdUnitSignals.instance().gdunit_event.connect(_on_gdunit_event)\n\tGdUnitSignals.instance().gdunit_message.connect(_on_gdunit_message)\n\tGdUnitSignals.instance().gdunit_client_connected.connect(_on_gdunit_client_connected)\n\tGdUnitSignals.instance().gdunit_client_disconnected.connect(_on_gdunit_client_disconnected)\n\toutput.clear()\n\n\nfunc _notification(what: int) -> void:\n\tif what == EditorSettings.NOTIFICATION_EDITOR_SETTINGS_CHANGED:\n\t\tinit_colors()\n\tif what == NOTIFICATION_PREDELETE:\n\t\tvar instance := GdUnitSignals.instance()\n\t\tif instance.gdunit_event.is_connected(_on_gdunit_event):\n\t\t\tinstance.gdunit_event.disconnect(_on_gdunit_event)\n\t\tif instance.gdunit_message.is_connected(_on_gdunit_event):\n\t\t\tinstance.gdunit_message.disconnect(_on_gdunit_message)\n\t\tif instance.gdunit_client_connected.is_connected(_on_gdunit_event):\n\t\t\tinstance.gdunit_client_connected.disconnect(_on_gdunit_client_connected)\n\t\tif instance.gdunit_client_disconnected.is_connected(_on_gdunit_event):\n\t\t\tinstance.gdunit_client_disconnected.disconnect(_on_gdunit_client_disconnected)\n\n\nfunc init_colors() -> void:\n\tvar settings := EditorInterface.get_editor_settings()\n\t_text_color = settings.get_setting(\"text_editor/theme/highlighting/text_color\")\n\t_function_color = settings.get_setting(\"text_editor/theme/highlighting/function_color\")\n\t_engine_type_color = settings.get_setting(\"text_editor/theme/highlighting/engine_type_color\")\n\n\nfunc init_statistics(event: GdUnitEvent) -> void:\n\t_statistics[\"total_count\"] = event.total_count()\n\t_statistics[\"error_count\"] = 0\n\t_statistics[\"failed_count\"] = 0\n\t_statistics[\"skipped_count\"] = 0\n\t_statistics[\"flaky_count\"] = 0\n\t_statistics[\"orphan_nodes\"] = 0\n\t_summary[\"total_count\"] += event.total_count()\n\n\nfunc reset_statistics() -> void:\n\tfor k: String in _statistics.keys():\n\t\t_statistics[k] = 0\n\tfor k: String in _summary.keys():\n\t\t_summary[k] = 0\n\n\nfunc update_statistics(event: GdUnitEvent) -> void:\n\t_statistics[\"error_count\"] += event.error_count()\n\t_statistics[\"failed_count\"] += event.failed_count()\n\t_statistics[\"skipped_count\"] += event.is_skipped() as int\n\t_statistics[\"flaky_count\"] += event.is_flaky() as int\n\t_statistics[\"orphan_nodes\"] += event.orphan_nodes()\n\t_summary[\"error_count\"] += event.error_count()\n\t_summary[\"failed_count\"] += event.failed_count()\n\t_summary[\"skipped_count\"] += event.is_skipped() as int\n\t_summary[\"flaky_count\"] += event.is_flaky() as int\n\t_summary[\"orphan_nodes\"] += event.orphan_nodes()\n\n\nfunc print_message(message: String, color: Color=_text_color, indent:=0) -> void:\n\tfor i in indent:\n\t\toutput.push_indent(1)\n\toutput.push_color(color)\n\toutput.append_text(message)\n\toutput.pop()\n\tfor i in indent:\n\t\toutput.pop()\n\n\nfunc println_message(message: String, color: Color=_text_color, indent:=-1) -> void:\n\tprint_message(message, color, indent)\n\toutput.newline()\n\n\nfunc line_number(report: GdUnitReport) -> String:\n\treturn str(report._line_number) if report._line_number != -1 else \"<n/a>\"\n\n\nfunc setup_update_notification(control: Button) -> void:\n\tif not GdUnitSettings.is_update_notification_enabled():\n\t\tprint_message(\"The search for updates is deactivated.\", Color.CORNFLOWER_BLUE)\n\t\treturn\n\n\tprintln_message(\"Searching for updates.\", Color.CORNFLOWER_BLUE)\n\tvar update_client := GdUnitUpdateClient.new()\n\tadd_child(update_client)\n\tvar response :GdUnitUpdateClient.HttpResponse = await update_client.request_latest_version()\n\tif response.status() != 200:\n\t\tprintln_message(\"Information cannot be retrieved from GitHub!\", Color.INDIAN_RED)\n\t\tprintln_message(\"Error:  %s\" % response.response(), Color.INDIAN_RED)\n\t\treturn\n\tvar latest_version := update_client.extract_latest_version(response)\n\tif not latest_version.is_greater(GdUnit4Version.current()):\n\t\tprintln_message(\"GdUnit4 is up-to-date.\", Color.FOREST_GREEN)\n\t\treturn\n\n\tprintln_message(\"A new update is available %s\" % latest_version, Color.YELLOW)\n\tprintln_message(\"Open the GdUnit4 settings and check the update tab.\", Color.YELLOW)\n\n\tcontrol.icon = GdUnitUiTools.get_icon(\"Notification\", Color.YELLOW)\n\tvar tween :=create_tween()\n\ttween.tween_property(control, \"self_modulate\", Color.VIOLET, .2).set_trans(Tween.TransitionType.TRANS_LINEAR)\n\ttween.tween_property(control, \"self_modulate\", Color.YELLOW, .2).set_trans(Tween.TransitionType.TRANS_BOUNCE)\n\ttween.parallel()\n\ttween.tween_property(control, \"scale\", Vector2.ONE*1.05, .4).set_trans(Tween.TransitionType.TRANS_LINEAR)\n\ttween.tween_property(control, \"scale\", Vector2.ONE, .4).set_trans(Tween.TransitionType.TRANS_BOUNCE)\n\ttween.set_loops(-1)\n\ttween.play()\n\n\nfunc _on_gdunit_event(event: GdUnitEvent) -> void:\n\tmatch event.type():\n\t\tGdUnitEvent.INIT:\n\t\t\treset_statistics()\n\n\t\tGdUnitEvent.STOP:\n\t\t\tprint_message(\"Summary:\", Color.DODGER_BLUE)\n\t\t\tprintln_message(\"| %d total | %d error | %d failed | %d flaky | %d skipped | %d orphans |\" %\\\n\t\t\t\t[_summary[\"total_count\"],\n\t\t\t\t_summary[\"error_count\"],\n\t\t\t\t_summary[\"failed_count\"],\n\t\t\t\t_summary[\"flaky_count\"],\n\t\t\t\t_summary[\"skipped_count\"],\n\t\t\t\t_summary[\"orphan_nodes\"]],\n\t\t\t\t_text_color, 1)\n\t\t\tprint_message(\"[wave][/wave]\")\n\n\t\tGdUnitEvent.TESTSUITE_BEFORE:\n\t\t\tinit_statistics(event)\n\t\t\tprint_message(\"Execute: \", Color.DODGER_BLUE)\n\t\t\tprintln_message(event._suite_name, _engine_type_color)\n\n\t\tGdUnitEvent.TESTSUITE_AFTER:\n\t\t\tif not event.reports().is_empty():\n\t\t\t\tprintln_message(\"\\t\" + event._suite_name, _engine_type_color)\n\t\t\t\tfor report: GdUnitReport in event.reports():\n\t\t\t\t\tprintln_message(\"line %s: %s\" % [line_number(report), report._message], _text_color, 2)\n\t\t\tif event.is_success() and event.is_flaky():\n\t\t\t\tprint_message(\"[wave]FLAKY[/wave]\", Color.GREEN_YELLOW)\n\t\t\telif event.is_success():\n\t\t\t\tprint_message(\"[wave]PASSED[/wave]\", Color.LIGHT_GREEN)\n\t\t\telse:\n\t\t\t\tprint_message(\"[shake rate=5 level=10][b]FAILED[/b][/shake]\", Color.FIREBRICK)\n\t\t\tprint_message(\" | %d total | %d error | %d failed | %d flaky | %d skipped | %d orphans |\" %\\\n\t\t\t\t[_statistics[\"total_count\"],\n\t\t\t\t_statistics[\"error_count\"],\n\t\t\t\t_statistics[\"failed_count\"],\n\t\t\t\t_statistics[\"flaky_count\"],\n\t\t\t\t_statistics[\"skipped_count\"],\n\t\t\t\t_statistics[\"orphan_nodes\"]])\n\t\t\tprintln_message(\"%+12s\" % LocalTime.elapsed(event.elapsed_time()))\n\t\t\tprintln_message(\" \")\n\n\n\t\tGdUnitEvent.TESTCASE_BEFORE:\n\t\t\tvar spaces := \"-%d\" % (80 - event._suite_name.length())\n\t\t\tprint_message(event._suite_name, _engine_type_color, 1)\n\t\t\tprint_message(\":\")\n\t\t\tprint_message((\"%\" + spaces + \"s\") % event._test_name, _function_color)\n\n\t\tGdUnitEvent.TESTCASE_AFTER:\n\t\t\tvar reports := event.reports()\n\t\t\tif event.is_flaky() and event.is_success():\n\t\t\t\tvar retries :int = event.statistic(GdUnitEvent.RETRY_COUNT)\n\t\t\t\tprint_message(\"[wave]FLAKY[/wave] (%d retries)\" % retries, Color.GREEN_YELLOW)\n\t\t\telif event.is_success():\n\t\t\t\tprint_message(\"PASSED\", Color.LIGHT_GREEN)\n\t\t\telif event.is_skipped():\n\t\t\t\tprint_message(\"SKIPPED\", Color.GOLDENROD)\n\t\t\telif event.is_error() or event.is_failed():\n\t\t\t\tvar retries :int = event.statistic(GdUnitEvent.RETRY_COUNT)\n\t\t\t\tif retries > 1:\n\t\t\t\t\tprint_message(\"[wave]FAILED[/wave] (retry %d)\" % retries, Color.FIREBRICK)\n\t\t\t\telse:\n\t\t\t\t\tprint_message(\"[wave]FAILED[/wave]\", Color.FIREBRICK)\n\t\t\telif event.is_warning():\n\t\t\t\tprint_message(\"WARNING\", Color.YELLOW)\n\t\t\tprintln_message(\" %+12s\" % LocalTime.elapsed(event.elapsed_time()))\n\n\t\t\tfor report: GdUnitReport in event.reports():\n\t\t\t\tprintln_message(\"line %s: %s\" % [line_number(report), report._message], _text_color, 2)\n\n\t\tGdUnitEvent.TESTCASE_STATISTICS:\n\t\t\tupdate_statistics(event)\n\n\nfunc _on_gdunit_client_connected(client_id: int) -> void:\n\toutput.clear()\n\toutput.append_text(\"[color=#9887c4]GdUnit Test Client connected with id %d[/color]\\n\" % client_id)\n\toutput.newline()\n\n\nfunc _on_gdunit_client_disconnected(client_id: int) -> void:\n\toutput.append_text(\"[color=#9887c4]GdUnit Test Client disconnected with id %d[/color]\\n\" % client_id)\n\toutput.newline()\n\n\nfunc _on_gdunit_message(message: String) -> void:\n\toutput.newline()\n\toutput.append_text(message)\n\toutput.newline()\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/GdUnitConsole.gd.uid",
    "content": "uid://cor0lin6n0qjf\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/GdUnitConsole.tscn",
    "content": "[gd_scene load_steps=2 format=3 uid=\"uid://dm0wvfyeew7vd\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/gdUnit4/src/ui/GdUnitConsole.gd\" id=\"1\"]\n\n[node name=\"Control\" type=\"Control\"]\nuse_parent_material = true\nclip_contents = true\ncustom_minimum_size = Vector2(0, 200)\nlayout_mode = 3\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\nscript = ExtResource(\"1\")\n\n[node name=\"VBoxContainer\" type=\"VBoxContainer\" parent=\".\"]\nuse_parent_material = true\nclip_contents = true\nlayout_mode = 1\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\n\n[node name=\"Header\" type=\"PanelContainer\" parent=\"VBoxContainer\"]\ncustom_minimum_size = Vector2(0, 32)\nlayout_mode = 2\nauto_translate = false\nlocalize_numeral_system = false\nmouse_filter = 2\n\n[node name=\"header_title\" type=\"RichTextLabel\" parent=\"VBoxContainer/Header\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\nauto_translate = false\nlocalize_numeral_system = false\nmouse_filter = 2\nbbcode_enabled = true\nscroll_active = false\nautowrap_mode = 0\nshortcut_keys_enabled = false\n\n[node name=\"Console\" type=\"ScrollContainer\" parent=\"VBoxContainer\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\n\n[node name=\"TextEdit\" type=\"RichTextLabel\" parent=\"VBoxContainer/Console\"]\nuse_parent_material = true\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\nbbcode_enabled = true\nscroll_following = true\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/GdUnitFonts.gd",
    "content": "class_name GdUnitFonts\nextends RefCounted\n\nconst FONT_MONO = \"res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Regular.ttf\"\nconst FONT_MONO_BOLT = \"res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Bold.ttf\"\nconst FONT_MONO_BOLT_ITALIC = \"res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-BoldItalic.ttf\"\nconst FONT_MONO_ITALIC = \"res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Italic.ttf\"\n\n\nstatic func init_fonts(item: CanvasItem) -> float:\n\t# add a default fallback font\n\titem.set(\"theme_override_fonts/font\", load_and_resize_font(FONT_MONO, 16))\n\titem.set(\"theme_override_fonts/normal_font\", load_and_resize_font(FONT_MONO, 16))\n\titem.set(\"theme_override_font_sizes/font_size\", 16)\n\tif Engine.is_editor_hint():\n\t\tvar settings := EditorInterface.get_editor_settings()\n\t\tvar scale_factor := EditorInterface.get_editor_scale()\n\t\tvar font_size: float = settings.get_setting(\"interface/editor/main_font_size\")\n\t\tfont_size *= scale_factor\n\t\tvar font_mono := load_and_resize_font(FONT_MONO, font_size)\n\t\titem.set(\"theme_override_fonts/normal_font\", font_mono)\n\t\titem.set(\"theme_override_fonts/bold_font\", load_and_resize_font(FONT_MONO_BOLT, font_size))\n\t\titem.set(\"theme_override_fonts/italics_font\", load_and_resize_font(FONT_MONO_ITALIC, font_size))\n\t\titem.set(\"theme_override_fonts/bold_italics_font\", load_and_resize_font(FONT_MONO_BOLT_ITALIC, font_size))\n\t\titem.set(\"theme_override_fonts/mono_font\", font_mono)\n\t\titem.set(\"theme_override_font_sizes/font_size\", font_size)\n\t\titem.set(\"theme_override_font_sizes/normal_font_size\", font_size)\n\t\titem.set(\"theme_override_font_sizes/bold_font_size\", font_size)\n\t\titem.set(\"theme_override_font_sizes/italics_font_size\", font_size)\n\t\titem.set(\"theme_override_font_sizes/bold_italics_font_size\", font_size)\n\t\titem.set(\"theme_override_font_sizes/mono_font_size\", font_size)\n\t\treturn font_size\n\treturn 16.0\n\n\nstatic func load_and_resize_font(font_resource: String, size: float) -> FontFile:\n\tvar font: FontFile = ResourceLoader.load(font_resource, \"FontFile\")\n\tif font == null:\n\t\tpush_error(\"Can't load font '%s'\" % font_resource)\n\t\treturn null\n\tvar resized_font: FontFile = font.duplicate()\n\tresized_font.fixed_size = int(size)\n\treturn resized_font\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/GdUnitFonts.gd.uid",
    "content": "uid://d3jb7jwfv4sv\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/GdUnitInspector.gd",
    "content": "@tool\nclass_name GdUnitInspecor\nextends Panel\n\nconst ScriptEditorContextMenuHandler = preload(\"res://addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandler.gd\")\nconst EditorFileSystemContextMenuHandler = preload(\"res://addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandler.gd\")\n\nvar _command_handler := GdUnitCommandHandler.instance()\n\n\nfunc _ready() -> void:\n\tif Engine.is_editor_hint():\n\t\t_getEditorThemes()\n\t@warning_ignore(\"return_value_discarded\")\n\tGdUnitCommandHandler.instance().gdunit_runner_start.connect(func() -> void:\n\t\tvar control :Control = get_parent_control()\n\t\t# if the tab is floating we dont need to set as current\n\t\tif control is TabContainer:\n\t\t\tvar tab_container :TabContainer = control\n\t\t\tfor tab_index in tab_container.get_tab_count():\n\t\t\t\tif tab_container.get_tab_title(tab_index) == \"GdUnit\":\n\t\t\t\t\ttab_container.set_current_tab(tab_index)\n\t)\n\tif Engine.is_editor_hint():\n\t\tadd_script_editor_context_menu()\n\t\tadd_file_system_dock_context_menu()\n\n\nfunc _process(_delta: float) -> void:\n\t_command_handler._do_process()\n\n\nfunc _getEditorThemes() -> void:\n\t# example to access current theme\n\t#var editiorTheme := interface.get_base_control().theme\n\t# setup inspector button icons\n\t#var stylebox_types :PackedStringArray = editiorTheme.get_stylebox_type_list()\n\t#for stylebox_type in stylebox_types:\n\t\t#prints(\"stylebox_type\", stylebox_type)\n\t#\tif \"Tree\" == stylebox_type:\n\t#\t\tprints(editiorTheme.get_stylebox_list(stylebox_type))\n\t#var style:StyleBoxFlat = editiorTheme.get_stylebox(\"panel\", \"Tree\")\n\t#style.bg_color = Color.RED\n\t#var locale = interface.get_editor_settings().get_setting(\"interface/editor/editor_language\")\n\t#sessions_label.add_theme_color_override(\"font_color\", get_color(\"contrast_color_2\", \"Editor\"))\n\t#status_label.add_theme_color_override(\"font_color\", get_color(\"contrast_color_2\", \"Editor\"))\n\t#no_sessions_label.add_theme_color_override(\"font_color\", get_color(\"contrast_color_2\", \"Editor\"))\n\tpass\n\n\n# Context menu registrations ----------------------------------------------------------------------\nfunc add_file_system_dock_context_menu() -> void:\n\tvar is_test_suite := func is_visible(script: Script, is_ts: bool) -> bool:\n\t\tif script == null:\n\t\t\treturn false\n\t\treturn GdObjects.is_test_suite(script) == is_ts\n\tvar menu :Array[GdUnitContextMenuItem] = [\n\t\tGdUnitContextMenuItem.new(GdUnitContextMenuItem.MENU_ID.TEST_RUN, \"Run Testsuites\", \"Play\", is_test_suite.bind(true), _command_handler.command(GdUnitCommandHandler.CMD_RUN_TESTSUITE)),\n\t\tGdUnitContextMenuItem.new(GdUnitContextMenuItem.MENU_ID.TEST_DEBUG, \"Debug Testsuites\", \"PlayStart\", is_test_suite.bind(true), _command_handler.command(GdUnitCommandHandler.CMD_RUN_TESTSUITE_DEBUG)),\n\t]\n\tadd_child(EditorFileSystemContextMenuHandler.new(menu))\n\n\nfunc add_script_editor_context_menu() -> void:\n\tvar is_test_suite := func is_visible(script: Script, is_ts: bool) -> bool:\n\t\treturn GdObjects.is_test_suite(script) == is_ts\n\tvar menu :Array[GdUnitContextMenuItem] = [\n\t\tGdUnitContextMenuItem.new(GdUnitContextMenuItem.MENU_ID.TEST_RUN, \"Run Tests\", \"Play\", is_test_suite.bind(true), _command_handler.command(GdUnitCommandHandler.CMD_RUN_TESTCASE)),\n\t\tGdUnitContextMenuItem.new(GdUnitContextMenuItem.MENU_ID.TEST_DEBUG, \"Debug Tests\", \"PlayStart\", is_test_suite.bind(true),_command_handler.command(GdUnitCommandHandler.CMD_RUN_TESTCASE_DEBUG)),\n\t\tGdUnitContextMenuItem.new(GdUnitContextMenuItem.MENU_ID.CREATE_TEST, \"Create Test\", \"New\", is_test_suite.bind(false), _command_handler.command(GdUnitCommandHandler.CMD_CREATE_TESTCASE))\n\t]\n\tadd_child(ScriptEditorContextMenuHandler.new(menu))\n\n\nfunc _on_MainPanel_run_testsuite(test_suite_paths: Array, debug: bool) -> void:\n\t_command_handler.cmd_run_test_suites(test_suite_paths, debug)\n\n\nfunc _on_MainPanel_run_testcase(resource_path: String, test_case: String, test_param_index: int, debug: bool) -> void:\n\t_command_handler.cmd_run_test_case(resource_path, test_case, test_param_index, debug)\n\n\n@warning_ignore(\"redundant_await\")\nfunc _on_status_bar_request_discover_tests() -> void:\n\tawait _command_handler.cmd_discover_tests()\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/GdUnitInspector.gd.uid",
    "content": "uid://deefoe66kqqj1\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/GdUnitInspector.tscn",
    "content": "[gd_scene load_steps=8 format=3 uid=\"uid://mpo5o6d4uybu\"]\n\n[ext_resource type=\"PackedScene\" uid=\"uid://dx7xy4dgi3wwb\" path=\"res://addons/gdUnit4/src/ui/parts/InspectorToolBar.tscn\" id=\"1\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://dva3tonxsxrlk\" path=\"res://addons/gdUnit4/src/ui/parts/InspectorProgressBar.tscn\" id=\"2\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://c22l4odk7qesc\" path=\"res://addons/gdUnit4/src/ui/parts/InspectorStatusBar.tscn\" id=\"3\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://djp8ait0bxpsc\" path=\"res://addons/gdUnit4/src/ui/parts/InspectorMonitor.tscn\" id=\"4\"]\n[ext_resource type=\"Script\" path=\"res://addons/gdUnit4/src/ui/GdUnitInspector.gd\" id=\"5\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://bqfpidewtpeg0\" path=\"res://addons/gdUnit4/src/ui/parts/InspectorTreePanel.tscn\" id=\"7\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://cn5mp3tmi2gb1\" path=\"res://addons/gdUnit4/src/network/GdUnitServer.tscn\" id=\"7_721no\"]\n\n[node name=\"GdUnit\" type=\"Panel\"]\nuse_parent_material = true\nclip_contents = true\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\nsize_flags_horizontal = 11\nsize_flags_vertical = 3\nfocus_mode = 2\nscript = ExtResource(\"5\")\n\n[node name=\"VBoxContainer\" type=\"VBoxContainer\" parent=\".\"]\nuse_parent_material = true\nclip_contents = true\nlayout_mode = 0\nanchor_right = 1.0\nanchor_bottom = 1.0\nsize_flags_vertical = 11\n\n[node name=\"Header\" type=\"VBoxContainer\" parent=\"VBoxContainer\"]\nuse_parent_material = true\nclip_contents = true\nlayout_mode = 2\nsize_flags_horizontal = 9\n\n[node name=\"ToolBar\" parent=\"VBoxContainer/Header\" instance=ExtResource(\"1\")]\nlayout_mode = 2\nsize_flags_vertical = 1\n\n[node name=\"ProgressBar\" parent=\"VBoxContainer/Header\" instance=ExtResource(\"2\")]\nlayout_mode = 2\nsize_flags_horizontal = 5\nmax_value = 0.0\n\n[node name=\"StatusBar\" parent=\"VBoxContainer/Header\" instance=ExtResource(\"3\")]\nlayout_mode = 2\nsize_flags_horizontal = 11\n\n[node name=\"MainPanel\" parent=\"VBoxContainer\" instance=ExtResource(\"7\")]\nlayout_mode = 2\n\n[node name=\"Monitor\" parent=\"VBoxContainer\" instance=ExtResource(\"4\")]\nlayout_mode = 2\n\n[node name=\"event_server\" parent=\".\" instance=ExtResource(\"7_721no\")]\n\n[connection signal=\"request_discover_tests\" from=\"VBoxContainer/Header/StatusBar\" to=\".\" method=\"_on_status_bar_request_discover_tests\"]\n[connection signal=\"select_error_next\" from=\"VBoxContainer/Header/StatusBar\" to=\"VBoxContainer/MainPanel\" method=\"_on_select_next_item_by_state\" binds= [6]]\n[connection signal=\"select_error_prevous\" from=\"VBoxContainer/Header/StatusBar\" to=\"VBoxContainer/MainPanel\" method=\"_on_select_previous_item_by_state\" binds= [6]]\n[connection signal=\"select_failure_next\" from=\"VBoxContainer/Header/StatusBar\" to=\"VBoxContainer/MainPanel\" method=\"_on_select_next_item_by_state\" binds= [5]]\n[connection signal=\"select_failure_prevous\" from=\"VBoxContainer/Header/StatusBar\" to=\"VBoxContainer/MainPanel\" method=\"_on_select_previous_item_by_state\" binds= [5]]\n[connection signal=\"select_flaky_next\" from=\"VBoxContainer/Header/StatusBar\" to=\"VBoxContainer/MainPanel\" method=\"_on_select_next_item_by_state\" binds= [4]]\n[connection signal=\"select_flaky_prevous\" from=\"VBoxContainer/Header/StatusBar\" to=\"VBoxContainer/MainPanel\" method=\"_on_select_previous_item_by_state\" binds= [4]]\n[connection signal=\"tree_view_mode_changed\" from=\"VBoxContainer/Header/StatusBar\" to=\"VBoxContainer/MainPanel\" method=\"_on_status_bar_tree_view_mode_changed\"]\n[connection signal=\"run_testcase\" from=\"VBoxContainer/MainPanel\" to=\".\" method=\"_on_MainPanel_run_testcase\"]\n[connection signal=\"run_testsuite\" from=\"VBoxContainer/MainPanel\" to=\".\" method=\"_on_MainPanel_run_testsuite\"]\n[connection signal=\"jump_to_orphan_nodes\" from=\"VBoxContainer/Monitor\" to=\"VBoxContainer/MainPanel\" method=\"select_first_orphan\"]\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd",
    "content": "class_name GdUnitInspectorTreeConstants\nextends RefCounted\n\n\n# the inspector panel presantation\nenum TREE_VIEW_MODE {\n\tTREE,\n\tFLAT\n}\n\n\n# The inspector sort modes\nenum SORT_MODE {\n\tUNSORTED,\n\tNAME_ASCENDING,\n\tNAME_DESCENDING,\n\tEXECUTION_TIME\n}\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd.uid",
    "content": "uid://d4b4mraqmw303\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/GdUnitUiTools.gd",
    "content": "class_name GdUnitUiTools\nextends RefCounted\n\n\nstatic var _spinner: AnimatedTexture\n\n\nenum ImageFlipMode {\n\tHORIZONTAl,\n\tVERITCAL\n}\n\n\n## Returns the icon by name, if it exists.\nstatic func get_icon(icon_name: String, color: = Color.BLACK) -> Texture2D:\n\tif not Engine.is_editor_hint():\n\t\treturn null\n\tvar icon := EditorInterface.get_base_control().get_theme_icon(icon_name, \"EditorIcons\")\n\tif icon == null:\n\t\treturn null\n\tif color != Color.BLACK:\n\t\ticon = _modulate_texture(icon, color)\n\treturn icon\n\n\n## Returns the icon flipped\nstatic func get_flipped_icon(icon_name: String, mode: = ImageFlipMode.HORIZONTAl) -> Texture2D:\n\tif not Engine.is_editor_hint():\n\t\treturn null\n\tvar icon := EditorInterface.get_base_control().get_theme_icon(icon_name, \"EditorIcons\")\n\tif icon == null:\n\t\treturn null\n\treturn ImageTexture.create_from_image(_flip_image(icon, mode))\n\n\nstatic func get_spinner() -> AnimatedTexture:\n\tif _spinner != null:\n\t\treturn _spinner\n\t_spinner = AnimatedTexture.new()\n\t_spinner.frames = 8\n\t_spinner.speed_scale = 2.5\n\tfor frame in _spinner.frames:\n\t\t_spinner.set_frame_texture(frame, get_icon(\"Progress%d\" % (frame+1)))\n\t\t_spinner.set_frame_duration(frame, 0.2)\n\treturn _spinner\n\n\nstatic func get_color_animated_icon(icon_name :String, from :Color, to :Color) -> AnimatedTexture:\n\tvar texture := AnimatedTexture.new()\n\ttexture.frames = 8\n\ttexture.speed_scale = 2.5\n\tvar color := from\n\tfor frame in texture.frames:\n\t\tcolor = lerp(color, to, .2)\n\t\ttexture.set_frame_texture(frame, get_icon(icon_name, color))\n\t\ttexture.set_frame_duration(frame, 0.2)\n\treturn texture\n\n\nstatic func get_run_overall_icon() -> Texture2D:\n\tif not Engine.is_editor_hint():\n\t\treturn null\n\tvar icon := EditorInterface.get_base_control().get_theme_icon(\"Play\", \"EditorIcons\")\n\tvar image := _merge_images(icon.get_image(), Vector2i(-2, 0), icon.get_image(), Vector2i(3, 0))\n\treturn ImageTexture.create_from_image(image)\n\n\nstatic func get_GDScript_icon(status: String, color: Color) -> Texture2D:\n\tif not Engine.is_editor_hint():\n\t\treturn null\n\tvar icon_a := EditorInterface.get_base_control().get_theme_icon(\"GDScript\", \"EditorIcons\")\n\tvar icon_b := EditorInterface.get_base_control().get_theme_icon(status, \"EditorIcons\")\n\tvar overlay_image := _modulate_image(icon_b.get_image(), color)\n\tvar image := _merge_images_scaled(icon_a.get_image(), Vector2i(0, 0), overlay_image, Vector2i(5, 5))\n\treturn ImageTexture.create_from_image(image)\n\n\nstatic func get_CSharpScript_icon(status: String, color: Color) -> Texture2D:\n\tif not Engine.is_editor_hint():\n\t\treturn null\n\tvar icon_a := EditorInterface.get_base_control().get_theme_icon(\"CSharpScript\", \"EditorIcons\")\n\tvar icon_b := EditorInterface.get_base_control().get_theme_icon(status, \"EditorIcons\")\n\tvar overlay_image := _modulate_image(icon_b.get_image(), color)\n\tvar image := _merge_images_scaled(icon_a.get_image(), Vector2i(0, 0), overlay_image, Vector2i(5, 5))\n\treturn ImageTexture.create_from_image(image)\n\n\nstatic func _modulate_texture(texture: Texture2D, color: Color) -> Texture2D:\n\tvar image := _modulate_image(texture.get_image(), color)\n\treturn ImageTexture.create_from_image(image)\n\n\nstatic func _modulate_image(image: Image, color: Color) -> Image:\n\tvar data: PackedByteArray = image.data[\"data\"]\n\tfor pixel in range(0, data.size(), 4):\n\t\tvar pixel_a := _to_color(data, pixel)\n\t\tif pixel_a.a8 != 0:\n\t\t\tpixel_a = pixel_a.lerp(color, .9)\n\t\tdata[pixel + 0] = pixel_a.r8\n\t\tdata[pixel + 1] = pixel_a.g8\n\t\tdata[pixel + 2] = pixel_a.b8\n\t\tdata[pixel + 3] = pixel_a.a8\n\tvar output_image := Image.new()\n\toutput_image.set_data(image.get_width(), image.get_height(), image.has_mipmaps(), image.get_format(), data)\n\treturn output_image\n\n\nstatic func _merge_images(image1: Image, offset1: Vector2i, image2: Image, offset2: Vector2i) -> Image:\n\t## we need to fix the image to have the same size to avoid merge conflicts\n\tif image1.get_height() < image2.get_height():\n\t\timage1.resize(image2.get_width(), image2.get_height())\n\t# Create a new Image for the merged result\n\tvar merged_image := Image.create(image1.get_width(), image1.get_height(), false, Image.FORMAT_RGBA8)\n\tmerged_image.blit_rect_mask(image1, image2, Rect2(Vector2.ZERO, image1.get_size()), offset1)\n\tmerged_image.blit_rect_mask(image1, image2, Rect2(Vector2.ZERO, image2.get_size()), offset2)\n\treturn merged_image\n\n\n@warning_ignore(\"narrowing_conversion\")\nstatic func _merge_images_scaled(image1: Image, offset1: Vector2i, image2: Image, offset2: Vector2i) -> Image:\n\t## we need to fix the image to have the same size to avoid merge conflicts\n\tif image1.get_height() < image2.get_height():\n\t\timage1.resize(image2.get_width(), image2.get_height())\n\t# Create a new Image for the merged result\n\tvar merged_image := Image.create(image1.get_width(), image1.get_height(), false, image1.get_format())\n\tmerged_image.blend_rect(image1, Rect2(Vector2.ZERO, image1.get_size()), offset1)\n\timage2.resize(image2.get_width()/1.3, image2.get_height()/1.3)\n\tmerged_image.blend_rect(image2, Rect2(Vector2.ZERO, image2.get_size()), offset2)\n\treturn merged_image\n\n\nstatic func _flip_image(texture: Texture2D, mode: ImageFlipMode) -> Image:\n\tvar flipped_image := Image.new()\n\tflipped_image.copy_from(texture.get_image())\n\tif mode == ImageFlipMode.VERITCAL:\n\t\tflipped_image.flip_x()\n\telse:\n\t\tflipped_image.flip_y()\n\treturn flipped_image\n\n\nstatic func _to_color(data: PackedByteArray, position: int) -> Color:\n\tvar pixel_a := Color()\n\tpixel_a.r8 = data[position + 0]\n\tpixel_a.g8 = data[position + 1]\n\tpixel_a.b8 = data[position + 2]\n\tpixel_a.a8 = data[position + 3]\n\treturn pixel_a\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/GdUnitUiTools.gd.uid",
    "content": "uid://wphg57v01mms\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/ScriptEditorControls.gd",
    "content": "# A tool to provide extended script editor functionallity\nclass_name ScriptEditorControls\nextends RefCounted\n\n# https://github.com/godotengine/godot/blob/master/editor/plugins/script_editor_plugin.h\n# the Editor menu popup items\nenum {\n\tFILE_NEW,\n\tFILE_NEW_TEXTFILE,\n\tFILE_OPEN,\n\tFILE_REOPEN_CLOSED,\n\tFILE_OPEN_RECENT,\n\tFILE_SAVE,\n\tFILE_SAVE_AS,\n\tFILE_SAVE_ALL,\n\tFILE_THEME,\n\tFILE_RUN,\n\tFILE_CLOSE,\n\tCLOSE_DOCS,\n\tCLOSE_ALL,\n\tCLOSE_OTHER_TABS,\n\tTOGGLE_SCRIPTS_PANEL,\n\tSHOW_IN_FILE_SYSTEM,\n\tFILE_COPY_PATH,\n\tFILE_TOOL_RELOAD_SOFT,\n\tSEARCH_IN_FILES,\n\tREPLACE_IN_FILES,\n\tSEARCH_HELP,\n\tSEARCH_WEBSITE,\n\tHELP_SEARCH_FIND,\n\tHELP_SEARCH_FIND_NEXT,\n\tHELP_SEARCH_FIND_PREVIOUS,\n\tWINDOW_MOVE_UP,\n\tWINDOW_MOVE_DOWN,\n\tWINDOW_NEXT,\n\tWINDOW_PREV,\n\tWINDOW_SORT,\n\tWINDOW_SELECT_BASE = 100\n}\n\n\n# Saves the given script and closes if requested by <close=true>\n# The script is saved when is opened in the editor.\n# The script is closed when <close> is set to true.\nstatic func save_an_open_script(script_path: String, close:=false) -> bool:\n\t#prints(\"save_an_open_script\", script_path, close)\n\tif !Engine.is_editor_hint():\n\t\treturn false\n\tvar editor := EditorInterface.get_script_editor()\n\tvar editor_popup := _menu_popup()\n\t# search for the script in all opened editor scrips\n\tfor open_script in editor.get_open_scripts():\n\t\tif open_script.resource_path == script_path:\n\t\t\t# select the script in the editor\n\t\t\tEditorInterface.edit_script(open_script, 0);\n\t\t\t# save and close\n\t\t\teditor_popup.id_pressed.emit(FILE_SAVE)\n\t\t\tif close:\n\t\t\t\teditor_popup.id_pressed.emit(FILE_CLOSE)\n\t\t\treturn true\n\treturn false\n\n\n# Saves all opened script\nstatic func save_all_open_script() -> void:\n\tif Engine.is_editor_hint():\n\t\t_menu_popup().id_pressed.emit(FILE_SAVE_ALL)\n\n\nstatic func close_open_editor_scripts() -> void:\n\tif Engine.is_editor_hint():\n\t\t_menu_popup().id_pressed.emit(CLOSE_ALL)\n\n\n# Edits the given script.\n# The script is openend in the current editor and selected in the file system dock.\n# The line and column on which to open the script can also be specified.\n# The script will be open with the user-configured editor for the script's language which may be an external editor.\nstatic func edit_script(script_path: String, line_number := -1) -> void:\n\tvar file_system := EditorInterface.get_resource_filesystem()\n\tfile_system.update_file(script_path)\n\tvar file_system_dock := EditorInterface.get_file_system_dock()\n\tfile_system_dock.navigate_to_path(script_path)\n\tEditorInterface.select_file(script_path)\n\tvar script: GDScript = load(script_path)\n\tEditorInterface.edit_script(script, line_number)\n\n\nstatic func _menu_popup() -> PopupMenu:\n\t@warning_ignore(\"unsafe_method_access\")\n\treturn EditorInterface.get_script_editor().get_child(0).get_child(0).get_child(0).get_popup()\n\n\nstatic func _print_menu(popup: PopupMenu) -> void:\n\tfor itemIndex in popup.item_count:\n\t\tprints(\"get_item_id\", popup.get_item_id(itemIndex))\n\t\tprints(\"get_item_accelerator\", popup.get_item_accelerator(itemIndex))\n\t\tprints(\"get_item_shortcut\", popup.get_item_shortcut(itemIndex))\n\t\tprints(\"get_item_text\", popup.get_item_text(itemIndex))\n\t\tprints()\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/ScriptEditorControls.gd.uid",
    "content": "uid://dreue4d4nwelf\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandler.gd",
    "content": "@tool\nextends Control\n\nvar _context_menus := Dictionary()\n\n\nfunc _init(context_menus: Array[GdUnitContextMenuItem]) -> void:\n\tset_name(\"EditorFileSystemContextMenuHandler\")\n\tfor menu in context_menus:\n\t\t_context_menus[menu.id] = menu\n\tvar popup := _menu_popup()\n\tvar file_tree := _file_tree()\n\t@warning_ignore(\"return_value_discarded\")\n\tpopup.about_to_popup.connect(on_context_menu_show.bind(popup, file_tree))\n\t@warning_ignore(\"return_value_discarded\")\n\tpopup.id_pressed.connect(on_context_menu_pressed.bind(file_tree))\n\n\nfunc on_context_menu_show(context_menu: PopupMenu, file_tree: Tree) -> void:\n\tcontext_menu.add_separator()\n\tvar current_index := context_menu.get_item_count()\n\tvar selected_test_suites := collect_testsuites(_context_menus.values()[0] as GdUnitContextMenuItem, file_tree)\n\n\tfor menu_id: int in _context_menus.keys():\n\t\tvar menu_item: GdUnitContextMenuItem = _context_menus[menu_id]\n\t\tif selected_test_suites.size() != 0:\n\t\t\tcontext_menu.add_item(menu_item.name, menu_id)\n\t\t\t#context_menu.set_item_icon_modulate(current_index, Color.MEDIUM_PURPLE)\n\t\t\tcontext_menu.set_item_disabled(current_index, !menu_item.is_enabled(null))\n\t\t\tcontext_menu.set_item_icon(current_index, GdUnitUiTools.get_icon(menu_item.icon))\n\t\t\tcurrent_index += 1\n\n\nfunc on_context_menu_pressed(id: int, file_tree: Tree) -> void:\n\tif !_context_menus.has(id):\n\t\treturn\n\tvar menu_item: GdUnitContextMenuItem = _context_menus[id]\n\tvar selected_test_suites := collect_testsuites(menu_item, file_tree)\n\tmenu_item.execute([selected_test_suites])\n\n\nfunc collect_testsuites(_menu_item: GdUnitContextMenuItem, file_tree: Tree) -> PackedStringArray:\n\tvar file_system := EditorInterface.get_resource_filesystem()\n\tvar selected_item := file_tree.get_selected()\n\tvar selected_test_suites := PackedStringArray()\n\n\twhile selected_item:\n\t\tvar resource_path: String = selected_item.get_metadata(0)\n\t\tvar file_type := file_system.get_file_type(resource_path)\n\t\tvar is_dir := DirAccess.dir_exists_absolute(resource_path)\n\t\tif is_dir:\n\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\tselected_test_suites.append(resource_path)\n\t\telif is_dir or file_type == \"GDScript\" or file_type == \"CSharpScript\":\n\t\t\t# find a performant way to check if the selected item a testsuite\n\t\t\tvar resource := ResourceLoader.load(resource_path, \"Script\", ResourceLoader.CACHE_MODE_REUSE)\n\t\t\tif _menu_item.is_visible(resource):\n\t\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t\tselected_test_suites.append(resource_path)\n\t\tselected_item = file_tree.get_next_selected(selected_item)\n\treturn selected_test_suites\n\n\nfunc _file_tree() -> Tree:\n\treturn GdObjects.find_nodes_by_class(EditorInterface.get_file_system_dock(), \"Tree\", true)[-1]\n\n\nfunc _menu_popup() -> PopupMenu:\n\treturn GdObjects.find_nodes_by_class(EditorInterface.get_file_system_dock(), \"PopupMenu\")[-1]\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandler.gd.uid",
    "content": "uid://y0y8jvm4qxmx\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/menu/GdUnitContextMenuItem.gd",
    "content": "class_name GdUnitContextMenuItem\n\nenum MENU_ID {\n\tUNDEFINED = 0,\n\tTEST_RUN = 1000,\n\tTEST_DEBUG = 1001,\n\tTEST_RERUN = 1002,\n\tCREATE_TEST = 1010,\n}\n\nvar id: MENU_ID = MENU_ID.UNDEFINED:\n\tset(value):\n\t\tid = value\n\tget:\n\t\treturn id\n\nvar name: StringName:\n\tset(value):\n\t\tname = value\n\tget:\n\t\treturn name\n\nvar command: GdUnitCommand:\n\tset(value):\n\t\tcommand = value\n\tget:\n\t\treturn command\n\nvar visible: Callable:\n\tset(value):\n\t\tvisible = value\n\tget:\n\t\treturn visible\n\nvar icon: String:\n\tset(value):\n\t\ticon = value\n\tget:\n\t\treturn icon\n\n\nfunc _init(p_id: MENU_ID, p_name: StringName, p_icon :String, p_is_visible: Callable, p_command: GdUnitCommand) -> void:\n\tassert(p_id != null, \"(%s) missing parameter 'MENU_ID'\" % p_name)\n\tassert(p_is_visible != null, \"(%s) missing parameter 'GdUnitCommand'\" % p_name)\n\tassert(p_command != null, \"(%s) missing parameter 'GdUnitCommand'\" % p_name)\n\tself.id = p_id\n\tself.name = p_name\n\tself.icon = p_icon\n\tself.command = p_command\n\tself.visible = p_is_visible\n\n\nfunc shortcut() -> Shortcut:\n\treturn GdUnitCommandHandler.instance().get_shortcut(command.shortcut)\n\n\nfunc is_enabled(script: Script) -> bool:\n\treturn command.is_enabled.call(script)\n\n\nfunc is_visible(script: Script) -> bool:\n\treturn visible.call(script)\n\n\nfunc execute(arguments:=[]) -> void:\n\tif arguments.is_empty():\n\t\tcommand.runnable.call()\n\telse:\n\t\tcommand.runnable.callv(arguments)\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/menu/GdUnitContextMenuItem.gd.uid",
    "content": "uid://ctew4bue7mmwf\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandler.gd",
    "content": "@tool\nextends Control\n\nvar _context_menus := Dictionary()\nvar _editor: ScriptEditor\n\n\nfunc _init(context_menus: Array[GdUnitContextMenuItem]) -> void:\n\tset_name(\"ScriptEditorContextMenuHandler\")\n\tfor menu in context_menus:\n\t\t_context_menus[menu.id] = menu\n\t_editor = EditorInterface.get_script_editor()\n\t@warning_ignore(\"return_value_discarded\")\n\t_editor.editor_script_changed.connect(on_script_changed)\n\ton_script_changed(active_script())\n\n\nfunc _input(event: InputEvent) -> void:\n\tif event is InputEventKey and event.is_pressed():\n\t\tfor action: GdUnitContextMenuItem in _context_menus.values():\n\t\t\tif action.shortcut().matches_event(event) and action.is_visible(active_script()):\n\t\t\t\t#if not has_editor_focus():\n\t\t\t\t#\treturn\n\t\t\t\taction.execute()\n\t\t\t\taccept_event()\n\t\t\t\treturn\n\n\nfunc has_editor_focus() -> bool:\n\treturn (Engine.get_main_loop() as SceneTree).root.gui_get_focus_owner() == active_base_editor()\n\n\nfunc on_script_changed(script: Script) -> void:\n\tif script is Script:\n\t\tvar popups: Array[Node] = GdObjects.find_nodes_by_class(active_editor(), \"PopupMenu\", true)\n\t\tfor popup: PopupMenu in popups:\n\t\t\tif not popup.about_to_popup.is_connected(on_context_menu_show):\n\t\t\t\tpopup.about_to_popup.connect(on_context_menu_show.bind(script, popup))\n\t\t\tif not popup.id_pressed.is_connected(on_context_menu_pressed):\n\t\t\t\tpopup.id_pressed.connect(on_context_menu_pressed)\n\n\nfunc on_context_menu_show(script: Script, context_menu: PopupMenu) -> void:\n\t#prints(\"on_context_menu_show\", _context_menus.keys(), context_menu, self)\n\tcontext_menu.add_separator()\n\tvar current_index := context_menu.get_item_count()\n\tfor menu_id: int in _context_menus.keys():\n\t\tvar menu_item: GdUnitContextMenuItem = _context_menus[menu_id]\n\t\tif menu_item.is_visible(script):\n\t\t\tcontext_menu.add_item(menu_item.name, menu_id)\n\t\t\tcontext_menu.set_item_disabled(current_index, !menu_item.is_enabled(script))\n\t\t\tcontext_menu.set_item_shortcut(current_index, menu_item.shortcut(), true)\n\t\t\tcurrent_index += 1\n\n\nfunc on_context_menu_pressed(id: int) -> void:\n\tif !_context_menus.has(id):\n\t\treturn\n\tvar menu_item: GdUnitContextMenuItem = _context_menus[id]\n\tmenu_item.execute()\n\n\nfunc active_editor() -> ScriptEditorBase:\n\treturn _editor.get_current_editor()\n\n\nfunc active_base_editor() -> TextEdit:\n\treturn active_editor().get_base_editor()\n\n\nfunc active_script() -> Script:\n\treturn _editor.get_current_script()\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandler.gd.uid",
    "content": "uid://1h61i6n5br66\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/parts/InspectorMonitor.gd",
    "content": "@tool\nextends PanelContainer\n\nsignal jump_to_orphan_nodes()\n\n@onready var ICON_GREEN := GdUnitUiTools.get_icon(\"Unlinked\", Color.WEB_GREEN)\n@onready var ICON_RED := GdUnitUiTools.get_color_animated_icon(\"Unlinked\", Color.YELLOW, Color.ORANGE_RED)\n\n@onready var _button_time: Button = %btn_time\n@onready var _time: Label = %time_value\n@onready var _orphans: Label = %orphan_value\n@onready var _orphan_button: Button = %btn_orphan\n\nvar total_elapsed_time := 0\nvar total_orphans := 0\n\n\nfunc _ready() -> void:\n\t@warning_ignore(\"return_value_discarded\")\n\tGdUnitSignals.instance().gdunit_event.connect(_on_gdunit_event)\n\t_time.text = \"\"\n\t_orphans.text = \"0\"\n\t_button_time.icon = GdUnitUiTools.get_icon(\"Time\")\n\t_orphan_button.icon = ICON_GREEN\n\n\nfunc status_changed(elapsed_time: int, orphan_nodes: int) -> void:\n\ttotal_elapsed_time += elapsed_time\n\ttotal_orphans += orphan_nodes\n\t_time.text = LocalTime.elapsed(total_elapsed_time)\n\t_orphans.text = str(total_orphans)\n\tif total_orphans > 0:\n\t\t_orphan_button.icon = ICON_RED\n\n\nfunc _on_gdunit_event(event: GdUnitEvent) -> void:\n\tmatch event.type():\n\t\tGdUnitEvent.INIT:\n\t\t\t_orphan_button.icon = ICON_GREEN\n\t\t\ttotal_elapsed_time = 0\n\t\t\ttotal_orphans = 0\n\t\t\tstatus_changed(0, 0)\n\t\tGdUnitEvent.TESTCASE_BEFORE:\n\t\t\tpass\n\t\tGdUnitEvent.TESTCASE_AFTER:\n\t\t\tstatus_changed(0, event.orphan_nodes())\n\t\tGdUnitEvent.TESTSUITE_BEFORE:\n\t\t\tpass\n\t\tGdUnitEvent.TESTSUITE_AFTER:\n\t\t\tstatus_changed(event.elapsed_time(), event.orphan_nodes())\n\n\nfunc _on_ToolButton_pressed() -> void:\n\tjump_to_orphan_nodes.emit()\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/parts/InspectorMonitor.gd.uid",
    "content": "uid://cyg6g7gxxpy8b\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/parts/InspectorMonitor.tscn",
    "content": "[gd_scene load_steps=6 format=3 uid=\"uid://djp8ait0bxpsc\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/gdUnit4/src/ui/parts/InspectorMonitor.gd\" id=\"3\"]\n\n[sub_resource type=\"Image\" id=\"Image_1hcll\"]\ndata = {\n\"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, 4, 227, 227, 227, 36, 227, 227, 227, 36, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 131, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 5, 225, 225, 225, 76, 224, 224, 224, 255, 224, 224, 224, 255, 226, 226, 226, 77, 255, 255, 255, 5, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 99, 224, 224, 224, 232, 224, 224, 224, 244, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 244, 224, 224, 224, 233, 224, 224, 224, 97, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 135, 224, 224, 224, 247, 224, 224, 224, 115, 234, 234, 234, 12, 224, 224, 224, 130, 224, 224, 224, 130, 234, 234, 234, 12, 225, 225, 225, 116, 224, 224, 224, 248, 224, 224, 224, 132, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 77, 224, 224, 224, 251, 224, 224, 224, 64, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 66, 224, 224, 224, 252, 225, 225, 225, 75, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 201, 224, 224, 224, 146, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 224, 224, 224, 146, 224, 224, 224, 106, 255, 255, 255, 0, 225, 225, 225, 150, 224, 224, 224, 195, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 24, 224, 224, 224, 255, 226, 226, 226, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 233, 233, 233, 23, 225, 225, 225, 166, 224, 224, 224, 237, 228, 228, 228, 47, 255, 255, 255, 0, 225, 225, 225, 51, 224, 224, 224, 255, 224, 224, 224, 16, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 67, 224, 224, 224, 255, 225, 225, 225, 215, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 0, 223, 223, 223, 239, 224, 224, 224, 253, 224, 224, 224, 49, 255, 255, 255, 0, 230, 230, 230, 30, 224, 224, 224, 230, 224, 224, 224, 255, 224, 224, 224, 49, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 41, 224, 224, 224, 255, 225, 225, 225, 101, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 139, 224, 224, 224, 139, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 5, 225, 225, 225, 117, 224, 224, 224, 255, 224, 224, 224, 33, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 6, 224, 224, 224, 240, 226, 226, 226, 87, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 96, 224, 224, 224, 236, 255, 255, 255, 3, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 143, 224, 224, 224, 211, 224, 224, 224, 8, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 232, 232, 232, 11, 224, 224, 224, 216, 225, 225, 225, 141, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 238, 238, 238, 15, 224, 224, 224, 220, 224, 224, 224, 178, 238, 238, 238, 15, 255, 255, 255, 0, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 227, 227, 227, 18, 224, 224, 224, 184, 224, 224, 224, 218, 238, 238, 238, 15, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 36, 224, 224, 224, 212, 224, 224, 224, 232, 225, 225, 225, 133, 224, 224, 224, 251, 224, 224, 224, 240, 225, 225, 225, 135, 224, 224, 224, 234, 224, 224, 224, 208, 225, 225, 225, 34, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 230, 230, 230, 10, 224, 224, 224, 107, 224, 224, 224, 197, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 196, 224, 224, 224, 104, 224, 224, 224, 8, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_ugpqy\"]\nimage = SubResource(\"Image_1hcll\")\n\n[sub_resource type=\"Image\" id=\"Image_3te4a\"]\ndata = {\n\"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, 22, 138, 22, 251, 255, 255, 255, 0, 255, 255, 255, 0, 22, 138, 22, 234, 22, 138, 22, 247, 22, 138, 22, 253, 22, 138, 22, 253, 22, 138, 22, 247, 22, 138, 22, 233, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 22, 138, 22, 251, 22, 138, 22, 236, 255, 255, 255, 0, 22, 138, 22, 255, 255, 255, 255, 0, 23, 138, 23, 233, 22, 138, 22, 254, 22, 138, 22, 255, 22, 138, 22, 255, 22, 138, 22, 255, 22, 138, 22, 255, 22, 138, 22, 253, 23, 138, 23, 233, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 22, 138, 22, 236, 22, 138, 22, 253, 22, 138, 22, 236, 22, 138, 22, 251, 255, 255, 255, 0, 22, 138, 22, 247, 22, 138, 22, 255, 22, 138, 22, 248, 22, 138, 22, 233, 23, 138, 23, 233, 22, 138, 22, 249, 22, 138, 22, 255, 22, 138, 22, 246, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 22, 138, 22, 236, 22, 138, 22, 251, 255, 255, 255, 0, 255, 255, 255, 0, 22, 138, 22, 249, 22, 138, 22, 253, 23, 138, 23, 232, 255, 255, 255, 0, 255, 255, 255, 0, 22, 138, 22, 234, 22, 138, 22, 255, 22, 138, 22, 253, 255, 255, 255, 0, 255, 255, 255, 0, 22, 138, 22, 251, 22, 138, 22, 255, 22, 138, 22, 251, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 24, 139, 24, 231, 23, 138, 23, 231, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 23, 138, 23, 234, 22, 138, 22, 255, 22, 138, 22, 253, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 23, 138, 23, 231, 23, 138, 23, 234, 22, 138, 22, 249, 22, 138, 22, 255, 22, 138, 22, 246, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 23, 138, 23, 233, 22, 138, 22, 247, 22, 138, 22, 249, 24, 139, 24, 231, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 23, 138, 23, 245, 22, 138, 22, 255, 22, 138, 22, 255, 22, 138, 22, 255, 22, 138, 22, 253, 23, 138, 23, 233, 255, 255, 255, 0, 255, 255, 255, 0, 22, 138, 22, 234, 22, 138, 22, 254, 22, 138, 22, 255, 22, 138, 22, 253, 23, 138, 23, 231, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 23, 138, 23, 241, 22, 138, 22, 253, 22, 138, 22, 253, 22, 138, 22, 246, 22, 138, 22, 233, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 22, 138, 22, 247, 22, 138, 22, 255, 22, 138, 22, 248, 23, 138, 23, 232, 255, 255, 255, 0, 255, 255, 255, 0, 23, 138, 23, 245, 23, 138, 23, 241, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 22, 138, 22, 253, 22, 138, 22, 255, 22, 138, 22, 233, 255, 255, 255, 0, 255, 255, 255, 0, 23, 138, 23, 231, 22, 138, 22, 255, 22, 138, 22, 253, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 22, 138, 22, 253, 22, 138, 22, 255, 23, 138, 23, 233, 255, 255, 255, 0, 255, 255, 255, 0, 23, 138, 23, 234, 22, 138, 22, 255, 22, 138, 22, 253, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 22, 138, 22, 247, 22, 138, 22, 255, 22, 138, 22, 249, 22, 138, 22, 234, 23, 138, 23, 234, 22, 138, 22, 249, 22, 138, 22, 255, 22, 138, 22, 246, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 22, 138, 22, 233, 22, 138, 22, 253, 22, 138, 22, 255, 22, 138, 22, 255, 22, 138, 22, 255, 22, 138, 22, 255, 22, 138, 22, 253, 22, 138, 22, 233, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 23, 138, 23, 233, 22, 138, 22, 246, 22, 138, 22, 253, 22, 138, 22, 253, 22, 138, 22, 246, 23, 138, 23, 233, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_nj5du\"]\nimage = SubResource(\"Image_3te4a\")\n\n[node name=\"Monitor\" type=\"PanelContainer\"]\nclip_contents = true\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\noffset_right = -793.0\noffset_bottom = -564.0\nsize_flags_horizontal = 9\nsize_flags_vertical = 9\nscript = ExtResource(\"3\")\n\n[node name=\"HBoxContainer\" type=\"HBoxContainer\" parent=\".\"]\nlayout_mode = 2\nsize_flags_vertical = 4\n\n[node name=\"timer\" type=\"HBoxContainer\" parent=\"HBoxContainer\"]\nlayout_mode = 2\n\n[node name=\"btn_time\" type=\"Button\" parent=\"HBoxContainer/timer\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 4\nauto_translate = false\nlocalize_numeral_system = false\ntooltip_text = \"Shows the total elapsed time of test execution.\"\nmouse_force_pass_scroll_events = false\nbutton_mask = 0\nshortcut_feedback = false\nshortcut_in_tooltip = false\ntext = \"Time\"\nicon = SubResource(\"ImageTexture_ugpqy\")\nflat = true\n\n[node name=\"time_value\" type=\"Label\" parent=\"HBoxContainer/timer\"]\nunique_name_in_owner = true\nuse_parent_material = true\nlayout_mode = 2\nsize_flags_horizontal = 3\nauto_translate = false\nlocalize_numeral_system = false\nmax_lines_visible = 1\n\n[node name=\"orphan\" type=\"HBoxContainer\" parent=\"HBoxContainer/timer\"]\nlayout_mode = 2\n\n[node name=\"btn_orphan\" type=\"Button\" parent=\"HBoxContainer/timer/orphan\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 4\nauto_translate = false\nlocalize_numeral_system = false\ntooltip_text = \"Shows the total orphan nodes detected.\"\ntext = \"Orphans\"\nicon = SubResource(\"ImageTexture_nj5du\")\n\n[node name=\"orphan_value\" type=\"Label\" parent=\"HBoxContainer/timer/orphan\"]\nunique_name_in_owner = true\nuse_parent_material = true\nlayout_mode = 2\nsize_flags_horizontal = 3\nauto_translate = false\nlocalize_numeral_system = false\ntext = \"0\"\nmax_lines_visible = 1\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd",
    "content": "@tool\nextends ProgressBar\n\n@onready var status: Label = $Label\n@onready var style: StyleBoxFlat = get(\"theme_override_styles/fill\")\n\n\nfunc _ready() -> void:\n\t@warning_ignore(\"return_value_discarded\")\n\tGdUnitSignals.instance().gdunit_event.connect(_on_gdunit_event)\n\tstyle.bg_color = Color.DARK_GREEN\n\tvalue = 0\n\tmax_value = 0\n\tupdate_text()\n\n\nfunc progress_init(p_max_value: int) -> void:\n\tvalue = 0\n\tmax_value = p_max_value\n\tstyle.bg_color = Color.DARK_GREEN\n\tupdate_text()\n\n\nfunc progress_update(p_value: int, is_failed: bool) -> void:\n\tvalue += p_value\n\tupdate_text()\n\tif is_failed:\n\t\tstyle.bg_color = Color.DARK_RED\n\n\nfunc update_text() -> void:\n\tstatus.text = \"%d:%d\" % [value, max_value]\n\n\nfunc _on_gdunit_event(event: GdUnitEvent) -> void:\n\tmatch event.type():\n\t\tGdUnitEvent.INIT:\n\t\t\tprogress_init(event.total_count())\n\n\t\tGdUnitEvent.DISCOVER_END:\n\t\t\tprogress_init(event.total_count())\n\n\t\tGdUnitEvent.TESTCASE_STATISTICS:\n\t\t\tprogress_update(1, event.is_failed() or event.is_error())\n\n\t\tGdUnitEvent.TESTSUITE_AFTER:\n\t\t\tprogress_update(0, event.is_failed() or event.is_error())\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd.uid",
    "content": "uid://c1uaovwyjutgp\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/parts/InspectorProgressBar.tscn",
    "content": "[gd_scene load_steps=3 format=3 uid=\"uid://dva3tonxsxrlk\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd\" id=\"1\"]\n\n[sub_resource type=\"StyleBoxFlat\" id=\"StyleBoxFlat_ayfir\"]\nbg_color = Color(0, 0.392157, 0, 1)\n\n[node name=\"ProgressBar\" type=\"ProgressBar\"]\ncustom_minimum_size = Vector2(0, 20)\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\nsize_flags_vertical = 9\ntheme_override_styles/fill = SubResource(\"StyleBoxFlat_ayfir\")\nrounded = true\nallow_greater = true\nshow_percentage = false\nscript = ExtResource(\"1\")\n\n[node name=\"Label\" type=\"Label\" parent=\".\"]\nuse_parent_material = true\nlayout_mode = 1\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\nsize_flags_vertical = 3\nhorizontal_alignment = 1\nvertical_alignment = 1\nmax_lines_visible = 1\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd",
    "content": "@tool\nextends PanelContainer\n\nsignal select_failure_next()\nsignal select_failure_prevous()\nsignal select_error_next()\nsignal select_error_prevous()\nsignal select_flaky_next()\nsignal select_flaky_prevous()\nsignal request_discover_tests()\n\n@warning_ignore(\"unused_signal\")\nsignal tree_view_mode_changed(flat :bool)\n\n@onready var _errors: Label = %error_value\n@onready var _failures: Label = %failure_value\n@onready var _flaky_value: Label = %flaky_value\n@onready var _button_failure_up: Button = %btn_failure_up\n@onready var _button_failure_down: Button = %btn_failure_down\n@onready var _button_sync: Button = %btn_tree_sync\n@onready var _button_view_mode: Button = %btn_tree_mode\n@onready var _button_sort_mode: Button = %btn_tree_sort\n\n@onready var _icon_errors: TextureRect = %icon_errors\n@onready var _icon_failures: TextureRect = %icon_failures\n@onready var _icon_flaky: TextureRect = %icon_flaky\n\nvar total_failed := 0\nvar total_errors := 0\nvar total_flaky := 0\n\n\nvar icon_mappings := {\n\t# tree sort modes\n\t0x100 + GdUnitInspectorTreeConstants.SORT_MODE.UNSORTED : GdUnitUiTools.get_icon(\"TripleBar\"),\n\t0x100 + GdUnitInspectorTreeConstants.SORT_MODE.NAME_ASCENDING : GdUnitUiTools.get_icon(\"Sort\"),\n\t0x100 + GdUnitInspectorTreeConstants.SORT_MODE.NAME_DESCENDING : GdUnitUiTools.get_flipped_icon(\"Sort\"),\n\t0x100 + GdUnitInspectorTreeConstants.SORT_MODE.EXECUTION_TIME : GdUnitUiTools.get_icon(\"History\"),\n\t# tree view modes\n\t0x200 + GdUnitInspectorTreeConstants.TREE_VIEW_MODE.TREE : GdUnitUiTools.get_icon(\"Tree\", Color.GHOST_WHITE),\n\t0x200 + GdUnitInspectorTreeConstants.TREE_VIEW_MODE.FLAT : GdUnitUiTools.get_icon(\"AnimationTrackGroup\", Color.GHOST_WHITE)\n}\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc _ready() -> void:\n\t_failures.text = \"0\"\n\t_errors.text = \"0\"\n\t_icon_failures.texture = GdUnitUiTools.get_icon(\"StatusError\", Color.SKY_BLUE)\n\t_icon_errors.texture = GdUnitUiTools.get_icon(\"StatusError\", Color.DARK_RED)\n\t_icon_flaky.texture = GdUnitUiTools.get_icon(\"CheckBox\", Color.GREEN_YELLOW)\n\n\t_button_failure_up.icon = GdUnitUiTools.get_icon(\"ArrowUp\")\n\t_button_failure_down.icon = GdUnitUiTools.get_icon(\"ArrowDown\")\n\t_button_sync.icon = GdUnitUiTools.get_icon(\"Loop\")\n\t_set_sort_mode_menu_options()\n\t_set_view_mode_menu_options()\n\tGdUnitSignals.instance().gdunit_event.connect(_on_gdunit_event)\n\tGdUnitSignals.instance().gdunit_settings_changed.connect(_on_settings_changed)\n\tvar command_handler := GdUnitCommandHandler.instance()\n\tcommand_handler.gdunit_runner_start.connect(_on_gdunit_runner_start)\n\tcommand_handler.gdunit_runner_stop.connect(_on_gdunit_runner_stop)\n\n\n\nfunc _set_sort_mode_menu_options() -> void:\n\t_button_sort_mode.icon = GdUnitUiTools.get_icon(\"Sort\")\n\t# construct context sort menu according to the available modes\n\tvar context_menu :PopupMenu = _button_sort_mode.get_popup()\n\tcontext_menu.clear()\n\n\tif not context_menu.index_pressed.is_connected(_on_sort_mode_changed):\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tcontext_menu.index_pressed.connect(_on_sort_mode_changed)\n\n\tvar configured_sort_mode := GdUnitSettings.get_inspector_tree_sort_mode()\n\tfor sort_mode: String in GdUnitInspectorTreeConstants.SORT_MODE.keys():\n\t\tvar enum_value :int =  GdUnitInspectorTreeConstants.SORT_MODE.get(sort_mode)\n\t\tvar icon :Texture2D = icon_mappings[0x100 + enum_value]\n\t\tcontext_menu.add_icon_check_item(icon, normalise(sort_mode), enum_value)\n\t\tcontext_menu.set_item_checked(enum_value, configured_sort_mode == enum_value)\n\n\nfunc _set_view_mode_menu_options() -> void:\n\t_button_view_mode.icon = GdUnitUiTools.get_icon(\"Tree\", Color.GHOST_WHITE)\n\t# construct context tree view menu according to the available modes\n\tvar context_menu :PopupMenu = _button_view_mode.get_popup()\n\tcontext_menu.clear()\n\n\tif not context_menu.index_pressed.is_connected(_on_tree_view_mode_changed):\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tcontext_menu.index_pressed.connect(_on_tree_view_mode_changed)\n\n\tvar configured_tree_view_mode := GdUnitSettings.get_inspector_tree_view_mode()\n\tfor tree_view_mode: String in GdUnitInspectorTreeConstants.TREE_VIEW_MODE.keys():\n\t\tvar enum_value :int =  GdUnitInspectorTreeConstants.TREE_VIEW_MODE.get(tree_view_mode)\n\t\tvar icon :Texture2D = icon_mappings[0x200 + enum_value]\n\t\tcontext_menu.add_icon_check_item(icon, normalise(tree_view_mode), enum_value)\n\t\tcontext_menu.set_item_checked(enum_value, configured_tree_view_mode == enum_value)\n\n\nfunc normalise(value: String) -> String:\n\tvar parts := value.to_lower().split(\"_\")\n\tparts[0] = parts[0].capitalize()\n\treturn \" \".join(parts)\n\n\nfunc status_changed(errors: int, failed: int, flaky: int) -> void:\n\ttotal_failed += failed\n\ttotal_errors += errors\n\ttotal_flaky += flaky\n\t_failures.text = str(total_failed)\n\t_errors.text = str(total_errors)\n\t_flaky_value.text = str(total_flaky)\n\n\nfunc disable_buttons(value :bool) -> void:\n\t_button_sync.set_disabled(value)\n\t_button_sort_mode.set_disabled(value)\n\t_button_view_mode.set_disabled(value)\n\n\nfunc _on_gdunit_event(event: GdUnitEvent) -> void:\n\tmatch event.type():\n\t\tGdUnitEvent.DISCOVER_START:\n\t\t\tdisable_buttons(true)\n\n\t\tGdUnitEvent.DISCOVER_END:\n\t\t\tdisable_buttons(false)\n\n\t\tGdUnitEvent.INIT:\n\t\t\ttotal_failed = 0\n\t\t\ttotal_errors = 0\n\t\t\ttotal_flaky = 0\n\t\t\tstatus_changed(0, 0, 0)\n\t\tGdUnitEvent.TESTCASE_BEFORE:\n\t\t\tpass\n\t\tGdUnitEvent.TESTCASE_STATISTICS:\n\t\t\tif event.is_error():\n\t\t\t\tstatus_changed(event.error_count(), 0, event.is_flaky())\n\t\t\telse:\n\t\t\t\tstatus_changed(0, event.failed_count(), event.is_flaky())\n\t\tGdUnitEvent.TESTSUITE_BEFORE:\n\t\t\tpass\n\t\tGdUnitEvent.TESTSUITE_AFTER:\n\t\t\tif event.is_error():\n\t\t\t\tstatus_changed(event.error_count(), 0, 0)\n\t\t\telse:\n\t\t\t\tstatus_changed(0, event.failed_count(), 0)\n\n\nfunc _on_btn_error_up_pressed() -> void:\n\tselect_error_prevous.emit()\n\n\nfunc _on_btn_error_down_pressed() -> void:\n\tselect_error_next.emit()\n\n\nfunc _on_failure_up_pressed() -> void:\n\tselect_failure_prevous.emit()\n\n\nfunc _on_failure_down_pressed() -> void:\n\tselect_failure_next.emit()\n\n\nfunc _on_btn_flaky_up_pressed() -> void:\n\tselect_flaky_prevous.emit()\n\n\nfunc _on_btn_flaky_down_pressed() -> void:\n\tselect_flaky_next.emit()\n\n\nfunc _on_tree_sync_pressed() -> void:\n\trequest_discover_tests.emit()\n\n\nfunc _on_sort_mode_changed(index: int) -> void:\n\tvar selected_sort_mode :GdUnitInspectorTreeConstants.SORT_MODE = GdUnitInspectorTreeConstants.SORT_MODE.values()[index]\n\tGdUnitSettings.set_inspector_tree_sort_mode(selected_sort_mode)\n\n\nfunc _on_tree_view_mode_changed(index: int) ->void:\n\tvar selected_tree_mode :GdUnitInspectorTreeConstants.TREE_VIEW_MODE = GdUnitInspectorTreeConstants.TREE_VIEW_MODE.values()[index]\n\tGdUnitSettings.set_inspector_tree_view_mode(selected_tree_mode)\n\n\n################################################################################\n# external signal receiver\n################################################################################\nfunc _on_gdunit_runner_start() -> void:\n\tdisable_buttons(true)\n\n\nfunc _on_gdunit_runner_stop(_client_id: int) -> void:\n\tdisable_buttons(false)\n\n\nfunc _on_settings_changed(property :GdUnitProperty) -> void:\n\tif property.name() == GdUnitSettings.INSPECTOR_TREE_SORT_MODE:\n\t\t_set_sort_mode_menu_options()\n\tif property.name() == GdUnitSettings.INSPECTOR_TREE_VIEW_MODE:\n\t\t_set_view_mode_menu_options()\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd.uid",
    "content": "uid://rwnlupt6fynh\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/parts/InspectorStatusBar.tscn",
    "content": "[gd_scene load_steps=32 format=3 uid=\"uid://c22l4odk7qesc\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd\" id=\"3\"]\n\n[sub_resource type=\"Image\" id=\"Image_knei0\"]\ndata = {\n\"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, 224, 224, 224, 160, 230, 230, 230, 10, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 213, 225, 225, 225, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 75, 224, 224, 224, 188, 224, 224, 224, 238, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 245, 224, 224, 224, 96, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 133, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 245, 226, 226, 226, 95, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 77, 224, 224, 224, 255, 224, 224, 224, 253, 225, 225, 225, 117, 224, 224, 224, 32, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 212, 225, 225, 225, 42, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 129, 226, 226, 226, 70, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 189, 224, 224, 224, 255, 224, 224, 224, 113, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 159, 230, 230, 230, 10, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 73, 224, 224, 224, 255, 224, 224, 224, 185, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 242, 224, 224, 224, 255, 224, 224, 224, 24, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 25, 224, 224, 224, 255, 224, 224, 224, 238, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 243, 224, 224, 224, 254, 233, 233, 233, 23, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 229, 229, 229, 29, 224, 224, 224, 255, 224, 224, 224, 236, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 189, 224, 224, 224, 255, 225, 225, 225, 68, 255, 255, 255, 0, 255, 255, 255, 0, 230, 230, 230, 10, 224, 224, 224, 160, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 121, 224, 224, 224, 255, 224, 224, 224, 181, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 72, 224, 224, 224, 121, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 43, 224, 224, 224, 213, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 36, 225, 225, 225, 124, 224, 224, 224, 254, 224, 224, 224, 255, 226, 226, 226, 70, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 96, 224, 224, 224, 245, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 125, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 95, 224, 224, 224, 245, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 237, 224, 224, 224, 185, 226, 226, 226, 70, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 42, 224, 224, 224, 213, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 230, 230, 230, 10, 225, 225, 225, 159, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_jvn24\"]\nimage = SubResource(\"Image_knei0\")\n\n[sub_resource type=\"Image\" id=\"Image_cetp0\"]\ndata = {\n\"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, 233, 233, 233, 23, 224, 224, 224, 198, 224, 224, 224, 201, 224, 224, 224, 24, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 233, 233, 233, 23, 224, 224, 224, 213, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 215, 224, 224, 224, 24, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 196, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 199, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 171, 224, 224, 224, 195, 224, 224, 224, 253, 224, 224, 224, 255, 224, 224, 224, 195, 225, 225, 225, 175, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 176, 224, 224, 224, 200, 224, 224, 224, 253, 224, 224, 224, 255, 225, 225, 225, 199, 224, 224, 224, 179, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 194, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 197, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 212, 232, 232, 232, 22, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 194, 224, 224, 224, 196, 232, 232, 232, 22, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_k82x4\"]\nimage = SubResource(\"Image_cetp0\")\n\n[sub_resource type=\"Image\" id=\"Image_f2x20\"]\ndata = {\n\"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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_bs7qq\"]\nimage = SubResource(\"Image_f2x20\")\n\n[sub_resource type=\"Image\" id=\"Image_k73x4\"]\ndata = {\n\"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, 231, 231, 231, 21, 224, 224, 224, 194, 224, 224, 224, 196, 232, 232, 232, 22, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 212, 232, 232, 232, 22, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 194, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 197, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 176, 224, 224, 224, 200, 224, 224, 224, 253, 224, 224, 224, 255, 225, 225, 225, 199, 224, 224, 224, 179, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 171, 224, 224, 224, 195, 224, 224, 224, 253, 224, 224, 224, 255, 224, 224, 224, 195, 225, 225, 225, 175, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 196, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 199, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 233, 233, 233, 23, 224, 224, 224, 213, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 215, 224, 224, 224, 24, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 233, 233, 233, 23, 224, 224, 224, 198, 224, 224, 224, 201, 224, 224, 224, 24, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_0ck6a\"]\nimage = SubResource(\"Image_k73x4\")\n\n[sub_resource type=\"Image\" id=\"Image_ujiln\"]\ndata = {\n\"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, 3, 224, 224, 224, 105, 224, 224, 224, 192, 224, 224, 224, 244, 224, 224, 224, 238, 224, 224, 224, 197, 224, 224, 224, 105, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 233, 233, 233, 23, 225, 225, 225, 207, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 198, 226, 226, 226, 26, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 6, 224, 224, 224, 205, 224, 224, 224, 255, 224, 224, 224, 218, 225, 225, 225, 83, 237, 237, 237, 14, 237, 237, 237, 14, 224, 224, 224, 82, 224, 224, 224, 220, 224, 224, 224, 255, 224, 224, 224, 197, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 102, 224, 224, 224, 255, 224, 224, 224, 218, 227, 227, 227, 18, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 224, 224, 224, 16, 224, 224, 224, 221, 224, 224, 224, 255, 225, 225, 225, 101, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 198, 224, 224, 224, 255, 225, 225, 225, 84, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 86, 224, 224, 224, 255, 224, 224, 224, 194, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 1, 255, 255, 255, 4, 224, 224, 224, 238, 224, 224, 224, 255, 227, 227, 227, 18, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 229, 229, 229, 19, 224, 224, 224, 255, 224, 224, 224, 233, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 160, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 159, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 230, 230, 230, 20, 224, 224, 224, 255, 224, 224, 224, 237, 255, 255, 255, 0, 255, 255, 255, 0, 230, 230, 230, 10, 224, 224, 224, 213, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 212, 230, 230, 230, 10, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 90, 224, 224, 224, 255, 224, 224, 224, 185, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 42, 224, 224, 224, 245, 224, 224, 224, 245, 225, 225, 225, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 232, 232, 232, 22, 224, 224, 224, 224, 224, 224, 224, 255, 224, 224, 224, 98, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 96, 226, 226, 226, 95, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 230, 230, 230, 20, 224, 224, 224, 88, 224, 224, 224, 221, 224, 224, 224, 255, 225, 225, 225, 199, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 200, 227, 227, 227, 18, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 236, 224, 224, 224, 195, 224, 224, 224, 96, 255, 255, 255, 5, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_t7ac1\"]\nimage = SubResource(\"Image_ujiln\")\n\n[sub_resource type=\"Image\" id=\"Image_6qet5\"]\ndata = {\n\"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, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 248, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 248, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_03vfp\"]\nimage = SubResource(\"Image_6qet5\")\n\n[sub_resource type=\"Image\" id=\"Image_atf74\"]\ndata = {\n\"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, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 248, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 248, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_fv3i4\"]\nimage = SubResource(\"Image_atf74\")\n\n[sub_resource type=\"Image\" id=\"Image_dd3uy\"]\ndata = {\n\"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, 249, 249, 255, 230, 246, 246, 252, 230, 249, 249, 255, 230, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 246, 246, 252, 237, 246, 246, 252, 255, 246, 246, 252, 248, 255, 255, 255, 0, 246, 246, 252, 254, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 246, 246, 252, 236, 246, 246, 252, 254, 246, 246, 252, 247, 255, 255, 255, 0, 246, 246, 252, 254, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 246, 246, 253, 231, 246, 246, 253, 232, 246, 246, 252, 230, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 246, 246, 252, 243, 246, 246, 252, 255, 246, 246, 252, 242, 246, 246, 252, 230, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 246, 246, 252, 242, 246, 246, 252, 253, 246, 246, 252, 241, 246, 246, 252, 230, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 246, 246, 252, 244, 246, 246, 252, 255, 246, 246, 252, 241, 246, 246, 252, 230, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 246, 246, 252, 244, 246, 246, 252, 255, 246, 246, 252, 241, 246, 246, 252, 230, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_ab51p\"]\nimage = SubResource(\"Image_dd3uy\")\n\n[sub_resource type=\"Image\" id=\"Image_gu8ck\"]\ndata = {\n\"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, 122, 111, 23, 255, 121, 107, 126, 255, 120, 108, 206, 255, 120, 107, 240, 255, 120, 107, 240, 255, 120, 108, 206, 255, 121, 107, 124, 255, 128, 116, 22, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 121, 108, 80, 255, 120, 107, 240, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 121, 107, 239, 255, 123, 109, 77, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 121, 108, 78, 255, 120, 107, 254, 255, 120, 107, 255, 255, 120, 107, 240, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 240, 255, 120, 107, 255, 255, 120, 107, 254, 255, 122, 109, 75, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 128, 116, 22, 255, 121, 107, 239, 255, 120, 107, 255, 255, 122, 107, 107, 255, 121, 109, 42, 255, 120, 107, 233, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 231, 255, 121, 108, 40, 255, 121, 107, 112, 255, 120, 107, 255, 255, 120, 107, 238, 255, 128, 115, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 121, 107, 124, 255, 120, 107, 255, 255, 120, 107, 240, 255, 121, 109, 42, 255, 255, 255, 0, 255, 121, 109, 42, 255, 120, 107, 233, 255, 120, 107, 232, 255, 124, 112, 41, 255, 255, 255, 0, 255, 125, 108, 45, 255, 120, 107, 242, 255, 120, 107, 255, 255, 120, 107, 119, 255, 255, 255, 0, 255, 255, 255, 0, 255, 121, 107, 207, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 233, 255, 121, 109, 42, 255, 255, 255, 0, 255, 121, 109, 42, 255, 121, 109, 42, 255, 255, 255, 0, 255, 125, 108, 45, 255, 120, 107, 235, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 202, 255, 255, 255, 0, 255, 255, 255, 0, 255, 120, 107, 242, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 233, 255, 121, 109, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 125, 108, 45, 255, 120, 107, 235, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 108, 237, 255, 255, 255, 0, 255, 255, 255, 0, 255, 120, 107, 242, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 232, 255, 121, 109, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 122, 110, 44, 255, 120, 107, 234, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 108, 237, 255, 255, 255, 0, 255, 255, 255, 0, 255, 121, 107, 207, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 231, 255, 124, 112, 41, 255, 255, 255, 0, 255, 125, 108, 45, 255, 122, 110, 44, 255, 255, 255, 0, 255, 125, 107, 43, 255, 120, 107, 233, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 200, 255, 255, 255, 0, 255, 255, 255, 0, 255, 120, 108, 123, 255, 120, 107, 255, 255, 120, 107, 240, 255, 121, 108, 40, 255, 255, 255, 0, 255, 125, 108, 45, 255, 120, 107, 235, 255, 120, 107, 234, 255, 125, 107, 43, 255, 255, 255, 0, 255, 125, 107, 43, 255, 120, 107, 242, 255, 120, 107, 255, 255, 121, 108, 116, 255, 255, 255, 0, 255, 255, 255, 0, 255, 128, 116, 22, 255, 120, 107, 238, 255, 120, 107, 255, 255, 121, 107, 112, 255, 125, 108, 45, 255, 120, 107, 235, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 233, 255, 125, 107, 43, 255, 120, 107, 117, 255, 120, 107, 255, 255, 120, 107, 235, 255, 128, 113, 18, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 121, 107, 76, 255, 120, 107, 254, 255, 120, 107, 255, 255, 120, 107, 242, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 242, 255, 120, 107, 255, 255, 120, 107, 253, 255, 120, 109, 70, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 123, 109, 77, 255, 121, 107, 239, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 236, 255, 122, 108, 71, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 121, 109, 21, 255, 121, 107, 122, 255, 121, 107, 203, 255, 120, 107, 238, 255, 120, 107, 238, 255, 120, 107, 202, 255, 120, 107, 119, 255, 128, 115, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_2rpr0\"]\nimage = SubResource(\"Image_gu8ck\")\n\n[sub_resource type=\"Image\" id=\"Image_1rlh2\"]\ndata = {\n\"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, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 255, 224, 224, 224, 255, 231, 231, 231, 21, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 195, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 224, 224, 224, 255, 224, 224, 224, 255, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 194, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 178, 224, 224, 224, 194, 230, 230, 230, 20, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 194, 224, 224, 224, 179, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 180, 224, 224, 224, 180, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_1oriu\"]\nimage = SubResource(\"Image_1rlh2\")\n\n[sub_resource type=\"Image\" id=\"Image_7053f\"]\ndata = {\n\"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, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 181, 224, 224, 224, 180, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 180, 224, 224, 224, 195, 231, 231, 231, 21, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 195, 224, 224, 224, 178, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 195, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 224, 224, 224, 255, 224, 224, 224, 255, 231, 231, 231, 21, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 194, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 255, 224, 224, 224, 255, 230, 230, 230, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_ikyhk\"]\nimage = SubResource(\"Image_7053f\")\n\n[sub_resource type=\"Image\" id=\"Image_we0dj\"]\ndata = {\n\"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, 147, 198, 223, 232, 147, 198, 222, 242, 147, 197, 222, 250, 147, 197, 222, 254, 147, 197, 222, 254, 147, 197, 222, 250, 147, 198, 222, 242, 147, 198, 223, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 222, 238, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 198, 222, 253, 147, 198, 222, 237, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 222, 237, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 198, 222, 237, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 223, 232, 147, 198, 222, 253, 147, 197, 222, 255, 147, 198, 222, 240, 147, 198, 222, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 222, 234, 147, 198, 222, 241, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 223, 232, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 222, 242, 147, 197, 222, 255, 147, 197, 222, 254, 147, 198, 222, 234, 255, 255, 255, 0, 147, 198, 222, 234, 147, 197, 222, 253, 147, 197, 222, 253, 147, 198, 223, 234, 255, 255, 255, 0, 147, 198, 222, 234, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 241, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 222, 250, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 222, 234, 255, 255, 255, 0, 147, 198, 222, 234, 147, 198, 222, 234, 255, 255, 255, 0, 147, 198, 222, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 250, 255, 255, 255, 0, 255, 255, 255, 0, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 222, 234, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 222, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 255, 255, 255, 0, 255, 255, 255, 0, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 222, 234, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 223, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 222, 250, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 223, 234, 255, 255, 255, 0, 147, 198, 222, 234, 147, 198, 223, 234, 255, 255, 255, 0, 147, 198, 222, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 250, 255, 255, 255, 0, 255, 255, 255, 0, 147, 197, 222, 242, 147, 197, 222, 255, 147, 197, 222, 254, 147, 198, 222, 234, 255, 255, 255, 0, 147, 198, 222, 234, 147, 197, 222, 253, 147, 197, 222, 253, 147, 198, 222, 234, 255, 255, 255, 0, 147, 198, 222, 234, 147, 197, 222, 254, 147, 197, 222, 255, 147, 198, 222, 241, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 223, 232, 147, 197, 222, 253, 147, 197, 222, 255, 147, 198, 222, 241, 147, 198, 222, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 222, 234, 147, 197, 222, 241, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 223, 231, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 222, 237, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 237, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 222, 237, 147, 198, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 222, 237, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 222, 232, 147, 198, 222, 242, 147, 198, 222, 250, 147, 197, 222, 253, 147, 197, 222, 253, 147, 197, 222, 250, 147, 197, 222, 241, 147, 198, 223, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_i2d73\"]\nimage = SubResource(\"Image_we0dj\")\n\n[sub_resource type=\"Image\" id=\"Image_u8t6h\"]\ndata = {\n\"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, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 255, 224, 224, 224, 255, 231, 231, 231, 21, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 195, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 224, 224, 224, 255, 224, 224, 224, 255, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 194, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 178, 224, 224, 224, 194, 230, 230, 230, 20, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 194, 224, 224, 224, 179, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 180, 224, 224, 224, 180, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_mph2m\"]\nimage = SubResource(\"Image_u8t6h\")\n\n[sub_resource type=\"Image\" id=\"Image_x3jpw\"]\ndata = {\n\"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, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 181, 224, 224, 224, 180, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 180, 224, 224, 224, 195, 231, 231, 231, 21, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 195, 224, 224, 224, 178, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 195, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 224, 224, 224, 255, 224, 224, 224, 255, 231, 231, 231, 21, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 194, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 255, 224, 224, 224, 255, 230, 230, 230, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_k6fqi\"]\nimage = SubResource(\"Image_x3jpw\")\n\n[sub_resource type=\"Image\" id=\"Image_oprg2\"]\ndata = {\n\"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, 144, 239, 151, 76, 142, 239, 151, 228, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 240, 152, 128, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 143, 239, 152, 229, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 240, 152, 128, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 244, 153, 45, 143, 239, 152, 175, 149, 255, 170, 12, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 244, 153, 45, 142, 239, 151, 235, 142, 239, 151, 255, 143, 240, 151, 130, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 244, 153, 45, 142, 239, 151, 235, 142, 239, 151, 255, 143, 240, 151, 177, 153, 255, 153, 5, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 144, 244, 155, 23, 151, 244, 151, 22, 255, 255, 255, 0, 142, 244, 153, 45, 142, 239, 151, 235, 142, 239, 151, 255, 143, 240, 151, 177, 153, 255, 153, 5, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 144, 244, 155, 23, 143, 239, 151, 213, 142, 239, 152, 212, 145, 240, 152, 67, 142, 239, 151, 235, 142, 239, 151, 255, 143, 240, 151, 177, 153, 255, 153, 5, 255, 255, 255, 0, 142, 240, 152, 128, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 151, 244, 151, 22, 142, 239, 152, 212, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 143, 240, 151, 177, 153, 255, 153, 5, 255, 255, 255, 0, 142, 240, 152, 128, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 146, 243, 158, 21, 143, 239, 151, 211, 142, 239, 151, 255, 143, 240, 151, 177, 153, 255, 153, 5, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 146, 243, 158, 21, 143, 239, 152, 141, 153, 255, 153, 5, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 228, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 240, 151, 225, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 143, 241, 154, 73, 142, 239, 151, 226, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 240, 151, 225, 142, 241, 153, 70, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_04e57\"]\nimage = SubResource(\"Image_oprg2\")\n\n[node name=\"StatusBar\" type=\"PanelContainer\"]\nclip_contents = true\nanchors_preset = 10\nanchor_right = 1.0\noffset_right = -807.0\noffset_bottom = 31.0\ngrow_horizontal = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 0\nscript = ExtResource(\"3\")\n\n[node name=\"VBoxContainer\" type=\"VBoxContainer\" parent=\".\"]\nlayout_mode = 2\n\n[node name=\"tree_tools\" type=\"HBoxContainer\" parent=\"VBoxContainer\"]\nlayout_mode = 2\nsize_flags_vertical = 0\n\n[node name=\"Label\" type=\"Label\" parent=\"VBoxContainer/tree_tools\"]\nlayout_mode = 2\nsize_flags_horizontal = 0\ntext = \"Statisitics\"\n\n[node name=\"tree_buttons\" type=\"HBoxContainer\" parent=\"VBoxContainer/tree_tools\"]\nlayout_mode = 2\nsize_flags_horizontal = 10\nsize_flags_vertical = 4\nalignment = 2\n\n[node name=\"VSeparator\" type=\"VSeparator\" parent=\"VBoxContainer/tree_tools/tree_buttons\"]\nlayout_mode = 2\n\n[node name=\"btn_tree_sync\" type=\"Button\" parent=\"VBoxContainer/tree_tools/tree_buttons\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntooltip_text = \"Run discover tests.\"\ndisabled = true\nicon = SubResource(\"ImageTexture_jvn24\")\n\n[node name=\"btn_tree_sort\" type=\"MenuButton\" parent=\"VBoxContainer/tree_tools/tree_buttons\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntooltip_text = \"Sets tree sorting mode.\"\ndisabled = true\nicon = SubResource(\"ImageTexture_k82x4\")\nflat = false\nitem_count = 4\npopup/item_0/text = \"Unsorted\"\npopup/item_0/icon = SubResource(\"ImageTexture_bs7qq\")\npopup/item_0/checkable = 1\npopup/item_1/text = \"Name ascending\"\npopup/item_1/icon = SubResource(\"ImageTexture_k82x4\")\npopup/item_1/checkable = 1\npopup/item_1/checked = true\npopup/item_1/id = 1\npopup/item_2/text = \"Name descending\"\npopup/item_2/icon = SubResource(\"ImageTexture_0ck6a\")\npopup/item_2/checkable = 1\npopup/item_2/id = 2\npopup/item_3/text = \"Execution time\"\npopup/item_3/icon = SubResource(\"ImageTexture_t7ac1\")\npopup/item_3/checkable = 1\npopup/item_3/id = 3\n\n[node name=\"btn_tree_mode\" type=\"MenuButton\" parent=\"VBoxContainer/tree_tools/tree_buttons\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntooltip_text = \"Sets tree presentaion mode.\"\ndisabled = true\nicon = SubResource(\"ImageTexture_03vfp\")\nflat = false\nitem_count = 2\npopup/item_0/text = \"Tree\"\npopup/item_0/icon = SubResource(\"ImageTexture_fv3i4\")\npopup/item_0/checkable = 1\npopup/item_0/checked = true\npopup/item_1/text = \"Flat\"\npopup/item_1/icon = SubResource(\"ImageTexture_ab51p\")\npopup/item_1/checkable = 1\npopup/item_1/id = 1\n\n[node name=\"HSeparator\" type=\"HSeparator\" parent=\"VBoxContainer\"]\nlayout_mode = 2\n\n[node name=\"status_bar\" type=\"HFlowContainer\" parent=\"VBoxContainer\"]\nlayout_mode = 2\nlast_wrap_alignment = 1\n\n[node name=\"errors\" type=\"HBoxContainer\" parent=\"VBoxContainer/status_bar\"]\nlayout_mode = 2\nsize_flags_vertical = 4\n\n[node name=\"error_value\" type=\"Label\" parent=\"VBoxContainer/status_bar/errors\"]\nunique_name_in_owner = true\nuse_parent_material = true\ncustom_minimum_size = Vector2(24, 0)\nlayout_mode = 2\nsize_flags_horizontal = 2\ntext = \"0\"\nhorizontal_alignment = 2\njustification_flags = 0\n\n[node name=\"icon_errors\" type=\"TextureRect\" parent=\"VBoxContainer/status_bar/errors\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_vertical = 4\ntexture = SubResource(\"ImageTexture_2rpr0\")\nstretch_mode = 2\n\n[node name=\"Label\" type=\"Label\" parent=\"VBoxContainer/status_bar/errors\"]\nlayout_mode = 2\ntext = \"Errors\"\njustification_flags = 0\n\n[node name=\"navigation\" type=\"HBoxContainer\" parent=\"VBoxContainer/status_bar/errors\"]\nauto_translate_mode = 2\nlayout_mode = 2\nsize_flags_horizontal = 4\nsize_flags_vertical = 4\nlocalize_numeral_system = false\n\n[node name=\"btn_error_up\" type=\"Button\" parent=\"VBoxContainer/status_bar/errors/navigation\"]\nlayout_mode = 2\nsize_flags_vertical = 3\ntooltip_text = \"Shows the total test errors.\"\nicon = SubResource(\"ImageTexture_1oriu\")\n\n[node name=\"btn_error_down\" type=\"Button\" parent=\"VBoxContainer/status_bar/errors/navigation\"]\nlayout_mode = 2\nsize_flags_horizontal = 0\nsize_flags_vertical = 3\ntooltip_text = \"Shows the total test errors.\"\nicon = SubResource(\"ImageTexture_ikyhk\")\n\n[node name=\"VSeparator\" type=\"VSeparator\" parent=\"VBoxContainer/status_bar\"]\nlayout_mode = 2\n\n[node name=\"failures\" type=\"HBoxContainer\" parent=\"VBoxContainer/status_bar\"]\nlayout_mode = 2\nsize_flags_vertical = 4\n\n[node name=\"failure_value\" type=\"Label\" parent=\"VBoxContainer/status_bar/failures\"]\nunique_name_in_owner = true\nuse_parent_material = true\ncustom_minimum_size = Vector2(24, 0)\nlayout_mode = 2\nsize_flags_horizontal = 0\ntext = \"0\"\nhorizontal_alignment = 2\nvertical_alignment = 1\njustification_flags = 160\nmax_lines_visible = 1\n\n[node name=\"icon_failures\" type=\"TextureRect\" parent=\"VBoxContainer/status_bar/failures\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_vertical = 4\ntexture = SubResource(\"ImageTexture_i2d73\")\nstretch_mode = 2\n\n[node name=\"Label\" type=\"Label\" parent=\"VBoxContainer/status_bar/failures\"]\nlayout_mode = 2\ntext = \"Failures\"\njustification_flags = 0\n\n[node name=\"navigation\" type=\"HBoxContainer\" parent=\"VBoxContainer/status_bar/failures\"]\nauto_translate_mode = 2\nlayout_mode = 2\nsize_flags_horizontal = 4\nsize_flags_vertical = 4\nlocalize_numeral_system = false\n\n[node name=\"btn_failure_up\" type=\"Button\" parent=\"VBoxContainer/status_bar/failures/navigation\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_vertical = 3\ntooltip_text = \"Shows the total test errors.\"\nicon = SubResource(\"ImageTexture_mph2m\")\n\n[node name=\"btn_failure_down\" type=\"Button\" parent=\"VBoxContainer/status_bar/failures/navigation\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 0\nsize_flags_vertical = 3\ntooltip_text = \"Shows the total test errors.\"\nicon = SubResource(\"ImageTexture_k6fqi\")\n\n[node name=\"VSeparator2\" type=\"VSeparator\" parent=\"VBoxContainer/status_bar\"]\nlayout_mode = 2\n\n[node name=\"flaky\" type=\"HBoxContainer\" parent=\"VBoxContainer/status_bar\"]\nlayout_mode = 2\nsize_flags_vertical = 4\n\n[node name=\"flaky_value\" type=\"Label\" parent=\"VBoxContainer/status_bar/flaky\"]\nunique_name_in_owner = true\nuse_parent_material = true\ncustom_minimum_size = Vector2(24, 0)\nlayout_mode = 2\nsize_flags_horizontal = 0\ntext = \"0\"\nhorizontal_alignment = 2\nvertical_alignment = 1\njustification_flags = 160\nmax_lines_visible = 1\n\n[node name=\"icon_flaky\" type=\"TextureRect\" parent=\"VBoxContainer/status_bar/flaky\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_vertical = 4\ntexture = SubResource(\"ImageTexture_04e57\")\nstretch_mode = 2\n\n[node name=\"Label\" type=\"Label\" parent=\"VBoxContainer/status_bar/flaky\"]\nlayout_mode = 2\ntext = \"Flaky\"\njustification_flags = 0\n\n[node name=\"navigation\" type=\"HBoxContainer\" parent=\"VBoxContainer/status_bar/flaky\"]\nauto_translate_mode = 2\nlayout_mode = 2\nsize_flags_horizontal = 4\nsize_flags_vertical = 4\nlocalize_numeral_system = false\n\n[node name=\"btn_flaky_up\" type=\"Button\" parent=\"VBoxContainer/status_bar/flaky/navigation\"]\nlayout_mode = 2\nsize_flags_vertical = 3\ntooltip_text = \"Shows the total test errors.\"\nicon = SubResource(\"ImageTexture_1oriu\")\n\n[node name=\"btn_flaky_down\" type=\"Button\" parent=\"VBoxContainer/status_bar/flaky/navigation\"]\nlayout_mode = 2\nsize_flags_horizontal = 0\nsize_flags_vertical = 3\ntooltip_text = \"Shows the total test errors.\"\nicon = SubResource(\"ImageTexture_ikyhk\")\n\n[connection signal=\"pressed\" from=\"VBoxContainer/tree_tools/tree_buttons/btn_tree_sync\" to=\".\" method=\"_on_tree_sync_pressed\"]\n[connection signal=\"pressed\" from=\"VBoxContainer/status_bar/errors/navigation/btn_error_up\" to=\".\" method=\"_on_btn_error_up_pressed\"]\n[connection signal=\"pressed\" from=\"VBoxContainer/status_bar/errors/navigation/btn_error_down\" to=\".\" method=\"_on_btn_error_down_pressed\"]\n[connection signal=\"pressed\" from=\"VBoxContainer/status_bar/failures/navigation/btn_failure_up\" to=\".\" method=\"_on_failure_up_pressed\"]\n[connection signal=\"pressed\" from=\"VBoxContainer/status_bar/failures/navigation/btn_failure_down\" to=\".\" method=\"_on_failure_down_pressed\"]\n[connection signal=\"pressed\" from=\"VBoxContainer/status_bar/flaky/navigation/btn_flaky_up\" to=\".\" method=\"_on_btn_flaky_up_pressed\"]\n[connection signal=\"pressed\" from=\"VBoxContainer/status_bar/flaky/navigation/btn_flaky_down\" to=\".\" method=\"_on_btn_flaky_down_pressed\"]\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/parts/InspectorToolBar.gd",
    "content": "@tool\nextends PanelContainer\n\nsignal run_overall_pressed(debug: bool)\nsignal run_pressed(debug: bool)\nsignal stop_pressed()\n\n@onready var _version_label: Control = %version\n@onready var _button_wiki: Button = %help\n@onready var _tool_button: Button = %tool\n@onready var _button_run_overall: Button = %run_overall\n@onready var _button_run: Button = %run\n@onready var _button_run_debug: Button = %debug\n@onready var _button_stop: Button = %stop\n\n\n\nconst SETTINGS_SHORTCUT_MAPPING := {\n\tGdUnitSettings.SHORTCUT_INSPECTOR_RERUN_TEST: GdUnitShortcut.ShortCut.RERUN_TESTS,\n\tGdUnitSettings.SHORTCUT_INSPECTOR_RERUN_TEST_DEBUG: GdUnitShortcut.ShortCut.RERUN_TESTS_DEBUG,\n\tGdUnitSettings.SHORTCUT_INSPECTOR_RUN_TEST_OVERALL: GdUnitShortcut.ShortCut.RUN_TESTS_OVERALL,\n\tGdUnitSettings.SHORTCUT_INSPECTOR_RUN_TEST_STOP: GdUnitShortcut.ShortCut.STOP_TEST_RUN,\n}\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc _ready() -> void:\n\tGdUnit4Version.init_version_label(_version_label)\n\tvar command_handler := GdUnitCommandHandler.instance()\n\trun_pressed.connect(command_handler._on_run_pressed)\n\trun_overall_pressed.connect(command_handler._on_run_overall_pressed)\n\tstop_pressed.connect(command_handler._on_stop_pressed)\n\tcommand_handler.gdunit_runner_start.connect(_on_gdunit_runner_start)\n\tcommand_handler.gdunit_runner_stop.connect(_on_gdunit_runner_stop)\n\tGdUnitSignals.instance().gdunit_settings_changed.connect(_on_gdunit_settings_changed)\n\tinit_buttons()\n\tinit_shortcuts(command_handler)\n\n\n\nfunc init_buttons() -> void:\n\t_button_run_overall.icon = GdUnitUiTools.get_run_overall_icon()\n\t_button_run_overall.visible = GdUnitSettings.is_inspector_toolbar_button_show()\n\t_button_run.icon = GdUnitUiTools.get_icon(\"Play\")\n\t_button_run_debug.icon = GdUnitUiTools.get_icon(\"PlayStart\")\n\t_button_stop.icon = GdUnitUiTools.get_icon(\"Stop\")\n\t_tool_button.icon = GdUnitUiTools.get_icon(\"Tools\")\n\t_button_wiki.icon = GdUnitUiTools.get_icon(\"HelpSearch\")\n\n\nfunc init_shortcuts(command_handler: GdUnitCommandHandler) -> void:\n\t_button_run.shortcut = command_handler.get_shortcut(GdUnitShortcut.ShortCut.RERUN_TESTS)\n\t_button_run_overall.shortcut = command_handler.get_shortcut(GdUnitShortcut.ShortCut.RUN_TESTS_OVERALL)\n\t_button_run_debug.shortcut = command_handler.get_shortcut(GdUnitShortcut.ShortCut.RERUN_TESTS_DEBUG)\n\t_button_stop.shortcut = command_handler.get_shortcut(GdUnitShortcut.ShortCut.STOP_TEST_RUN)\n\t# register for shortcut changes\n\t@warning_ignore(\"return_value_discarded\")\n\tGdUnitSignals.instance().gdunit_settings_changed.connect(_on_settings_changed.bind(command_handler))\n\n\nfunc _on_runoverall_pressed(debug:=false) -> void:\n\trun_overall_pressed.emit(debug)\n\n\nfunc _on_run_pressed(debug := false) -> void:\n\trun_pressed.emit(debug)\n\n\nfunc _on_stop_pressed() -> void:\n\tstop_pressed.emit()\n\n\nfunc _on_gdunit_runner_start() -> void:\n\t_button_run_overall.disabled = true\n\t_button_run.disabled = true\n\t_button_run_debug.disabled = true\n\t_button_stop.disabled = false\n\n\nfunc _on_gdunit_runner_stop(_client_id: int) -> void:\n\t_button_run_overall.disabled = false\n\t_button_run.disabled = false\n\t_button_run_debug.disabled = false\n\t_button_stop.disabled = true\n\n\nfunc _on_gdunit_settings_changed(_property: GdUnitProperty) -> void:\n\t_button_run_overall.visible = GdUnitSettings.is_inspector_toolbar_button_show()\n\n\nfunc _on_wiki_pressed() -> void:\n\t@warning_ignore(\"return_value_discarded\")\n\tOS.shell_open(\"https://mikeschulze.github.io/gdUnit4/\")\n\n\nfunc _on_btn_tool_pressed() -> void:\n\tvar settings_dlg: Window = EditorInterface.get_base_control().find_child(\"GdUnitSettingsDialog\", false, false)\n\tif settings_dlg == null:\n\t\tsettings_dlg = preload(\"res://addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.tscn\").instantiate()\n\t\tEditorInterface.get_base_control().add_child(settings_dlg, true)\n\tsettings_dlg.popup_centered_ratio(.60)\n\n\nfunc _on_settings_changed(property: GdUnitProperty, command_handler: GdUnitCommandHandler) -> void:\n\t# needs to wait a frame to be command handler notified first for settings changes\n\tawait get_tree().process_frame\n\tif SETTINGS_SHORTCUT_MAPPING.has(property.name()):\n\t\tvar shortcut: GdUnitShortcut.ShortCut = SETTINGS_SHORTCUT_MAPPING.get(property.name(), GdUnitShortcut.ShortCut.NONE)\n\t\tmatch shortcut:\n\t\t\tGdUnitShortcut.ShortCut.RERUN_TESTS:\n\t\t\t\t_button_run.shortcut = command_handler.get_shortcut(shortcut)\n\t\t\tGdUnitShortcut.ShortCut.RUN_TESTS_OVERALL:\n\t\t\t\t_button_run_overall.shortcut = command_handler.get_shortcut(shortcut)\n\t\t\tGdUnitShortcut.ShortCut.RERUN_TESTS_DEBUG:\n\t\t\t\t_button_run_debug.shortcut = command_handler.get_shortcut(shortcut)\n\t\t\tGdUnitShortcut.ShortCut.STOP_TEST_RUN:\n\t\t\t\t_button_stop.shortcut = command_handler.get_shortcut(shortcut)\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/parts/InspectorToolBar.gd.uid",
    "content": "uid://cw4oleepl5xnr\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/parts/InspectorToolBar.tscn",
    "content": "[gd_scene load_steps=22 format=3 uid=\"uid://dx7xy4dgi3wwb\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/gdUnit4/src/ui/parts/InspectorToolBar.gd\" id=\"3\"]\n\n[sub_resource type=\"Image\" id=\"Image_walbn\"]\ndata = {\n\"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, 227, 227, 227, 36, 224, 224, 224, 168, 224, 224, 224, 233, 224, 224, 224, 236, 224, 224, 224, 170, 231, 231, 231, 31, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 36, 224, 224, 224, 234, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 239, 230, 230, 230, 30, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 168, 224, 224, 224, 255, 224, 224, 224, 186, 224, 224, 224, 32, 224, 224, 224, 33, 224, 224, 224, 187, 224, 224, 224, 255, 225, 225, 225, 167, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 237, 224, 224, 224, 255, 224, 224, 224, 33, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 36, 224, 224, 224, 255, 224, 224, 224, 234, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 237, 224, 224, 224, 255, 224, 224, 224, 33, 255, 255, 255, 0, 255, 255, 255, 0, 229, 229, 229, 38, 224, 224, 224, 255, 224, 224, 224, 229, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 164, 224, 224, 224, 255, 224, 224, 224, 187, 225, 225, 225, 34, 227, 227, 227, 36, 224, 224, 224, 192, 224, 224, 224, 255, 224, 224, 224, 162, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 24, 225, 225, 225, 215, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 229, 224, 224, 224, 32, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 24, 224, 224, 224, 216, 224, 224, 224, 255, 224, 224, 224, 210, 224, 224, 224, 161, 224, 224, 224, 232, 224, 224, 224, 231, 225, 225, 225, 159, 230, 230, 230, 30, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 107, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 105, 230, 230, 230, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 221, 224, 224, 224, 130, 255, 255, 255, 1, 255, 255, 255, 1, 225, 225, 225, 134, 224, 224, 224, 224, 225, 225, 225, 223, 224, 224, 224, 132, 255, 255, 255, 1, 255, 255, 255, 6, 224, 224, 224, 137, 224, 224, 224, 231, 224, 224, 224, 255, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 130, 225, 225, 225, 133, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 129, 224, 224, 224, 137, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 65, 224, 224, 224, 255, 224, 224, 224, 220, 225, 225, 225, 223, 224, 224, 224, 255, 226, 226, 226, 61, 224, 224, 224, 65, 224, 224, 224, 255, 224, 224, 224, 222, 224, 224, 224, 231, 224, 224, 224, 255, 227, 227, 227, 62, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 67, 224, 224, 224, 255, 224, 224, 224, 219, 224, 224, 224, 222, 224, 224, 224, 255, 227, 227, 227, 63, 225, 225, 225, 67, 224, 224, 224, 255, 224, 224, 224, 219, 224, 224, 224, 230, 224, 224, 224, 255, 227, 227, 227, 63, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 127, 224, 224, 224, 129, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 126, 225, 225, 225, 135, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 221, 225, 225, 225, 127, 255, 255, 255, 0, 255, 255, 255, 1, 224, 224, 224, 128, 224, 224, 224, 220, 224, 224, 224, 219, 225, 225, 225, 127, 255, 255, 255, 0, 255, 255, 255, 5, 225, 225, 225, 134, 224, 224, 224, 229, 224, 224, 224, 255, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_t52y3\"]\nimage = SubResource(\"Image_walbn\")\n\n[sub_resource type=\"Image\" id=\"Image_113nf\"]\ndata = {\n\"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, 224, 224, 224, 64, 224, 224, 224, 255, 227, 227, 227, 63, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 5, 225, 225, 225, 142, 255, 255, 255, 0, 255, 255, 255, 2, 224, 224, 224, 138, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 192, 224, 224, 224, 255, 225, 225, 225, 191, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 142, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 2, 224, 224, 224, 255, 224, 224, 224, 137, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 192, 224, 224, 224, 255, 225, 225, 225, 191, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 236, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 64, 224, 224, 224, 255, 227, 227, 227, 63, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 235, 224, 224, 224, 255, 224, 224, 224, 65, 225, 225, 225, 67, 224, 224, 224, 255, 224, 224, 224, 230, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 140, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 134, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 224, 224, 224, 137, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 135, 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, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 247, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 246, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 183, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 179, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 179, 224, 224, 224, 237, 224, 224, 224, 179, 230, 230, 230, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 190, 224, 224, 224, 188, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_wip2b\"]\nimage = SubResource(\"Image_113nf\")\n\n[sub_resource type=\"InputEventKey\" id=\"InputEventKey_6jdrj\"]\nctrl_pressed = true\npressed = true\nkeycode = 4194338\nphysical_keycode = 4194338\n\n[sub_resource type=\"Shortcut\" id=\"Shortcut_t0ytp\"]\nevents = [SubResource(\"InputEventKey_6jdrj\")]\n\n[sub_resource type=\"Image\" id=\"Image_fj3b5\"]\ndata = {\n\"data\": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 195, 224, 224, 224, 210, 224, 224, 224, 56, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 195, 224, 224, 224, 210, 224, 224, 224, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 253, 224, 224, 224, 139, 224, 224, 224, 8, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 253, 224, 224, 224, 139, 224, 224, 224, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 215, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 215, 224, 224, 224, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 253, 224, 224, 224, 139, 224, 224, 224, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 183, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 182, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 252, 225, 225, 225, 134, 255, 255, 255, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 212, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 212, 226, 226, 226, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 252, 225, 225, 225, 134, 255, 255, 255, 6, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 252, 225, 225, 225, 134, 255, 255, 255, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 225, 225, 225, 191, 224, 224, 224, 206, 226, 226, 226, 52, 0, 0, 0, 0, 0, 0, 0, 0, 225, 225, 225, 191, 224, 224, 224, 206, 226, 226, 226, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_mggmt\"]\nimage = SubResource(\"Image_fj3b5\")\n\n[sub_resource type=\"InputEventKey\" id=\"InputEventKey_pl3pi\"]\nctrl_pressed = true\npressed = true\nkeycode = 4194336\nphysical_keycode = 4194336\n\n[sub_resource type=\"Shortcut\" id=\"Shortcut_77xhn\"]\nevents = [SubResource(\"InputEventKey_pl3pi\")]\n\n[sub_resource type=\"Image\" id=\"Image_4apvt\"]\ndata = {\n\"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, 224, 224, 224, 195, 224, 224, 224, 210, 224, 224, 224, 56, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 253, 224, 224, 224, 139, 224, 224, 224, 8, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 215, 224, 224, 224, 56, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 253, 224, 224, 224, 139, 224, 224, 224, 8, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 183, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 182, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 252, 225, 225, 225, 134, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 212, 226, 226, 226, 52, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 252, 225, 225, 225, 134, 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, 225, 225, 225, 191, 224, 224, 224, 206, 226, 226, 226, 52, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_q6qbp\"]\nimage = SubResource(\"Image_4apvt\")\n\n[sub_resource type=\"InputEventKey\" id=\"InputEventKey_qk8q5\"]\nctrl_pressed = true\npressed = true\nkeycode = 4194337\nphysical_keycode = 4194337\n\n[sub_resource type=\"Shortcut\" id=\"Shortcut_ae6em\"]\nevents = [SubResource(\"InputEventKey_qk8q5\")]\n\n[sub_resource type=\"Image\" id=\"Image_jc2ll\"]\ndata = {\n\"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, 224, 224, 224, 184, 224, 224, 224, 255, 224, 224, 224, 181, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 184, 224, 224, 224, 202, 228, 228, 228, 37, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 239, 224, 224, 224, 74, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 253, 224, 224, 224, 123, 255, 255, 255, 1, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 173, 234, 234, 234, 12, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 188, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 185, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 168, 230, 230, 230, 10, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 252, 225, 225, 225, 118, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 237, 226, 226, 226, 70, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 181, 224, 224, 224, 255, 224, 224, 224, 180, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 188, 224, 224, 224, 201, 225, 225, 225, 34, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_q0wt0\"]\nimage = SubResource(\"Image_jc2ll\")\n\n[sub_resource type=\"InputEventKey\" id=\"InputEventKey_l8obn\"]\nctrl_pressed = true\npressed = true\nkeycode = 4194339\nphysical_keycode = 4194339\n\n[sub_resource type=\"Shortcut\" id=\"Shortcut_2mb87\"]\nevents = [SubResource(\"InputEventKey_l8obn\")]\n\n[sub_resource type=\"Image\" id=\"Image_ib7wg\"]\ndata = {\n\"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, 224, 224, 224, 176, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 177, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 177, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 176, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_1wiyx\"]\nimage = SubResource(\"Image_ib7wg\")\n\n[node name=\"ToolBar\" type=\"PanelContainer\"]\nanchors_preset = 10\nanchor_right = 1.0\noffset_right = -894.0\noffset_bottom = 24.0\ngrow_horizontal = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 4\nscript = ExtResource(\"3\")\n\n[node name=\"HBoxContainer\" type=\"HBoxContainer\" parent=\".\"]\nlayout_mode = 2\n\n[node name=\"tools\" type=\"HBoxContainer\" parent=\"HBoxContainer\"]\nlayout_mode = 2\nsize_flags_horizontal = 0\nsize_flags_vertical = 4\n\n[node name=\"help\" type=\"Button\" parent=\"HBoxContainer/tools\"]\nunique_name_in_owner = true\nlayout_mode = 2\nicon = SubResource(\"ImageTexture_t52y3\")\n\n[node name=\"tool\" type=\"Button\" parent=\"HBoxContainer/tools\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntooltip_text = \"GdUnit Settings\"\nicon = SubResource(\"ImageTexture_wip2b\")\n\n[node name=\"controls\" type=\"HBoxContainer\" parent=\"HBoxContainer\"]\nlayout_mode = 2\nsize_flags_horizontal = 6\nsize_flags_vertical = 4\nalignment = 1\n\n[node name=\"VSeparator3\" type=\"VSeparator\" parent=\"HBoxContainer/controls\"]\nlayout_mode = 2\n\n[node name=\"run_overall\" type=\"Button\" parent=\"HBoxContainer/controls\"]\nunique_name_in_owner = true\nvisible = false\nuse_parent_material = true\nlayout_mode = 2\ntooltip_text = \"Run overall tests\"\nshortcut = SubResource(\"Shortcut_t0ytp\")\nicon = SubResource(\"ImageTexture_mggmt\")\n\n[node name=\"run\" type=\"Button\" parent=\"HBoxContainer/controls\"]\nunique_name_in_owner = true\nuse_parent_material = true\nlayout_mode = 2\ntooltip_text = \"Rerun unit tests\"\nshortcut = SubResource(\"Shortcut_77xhn\")\nicon = SubResource(\"ImageTexture_q6qbp\")\n\n[node name=\"debug\" type=\"Button\" parent=\"HBoxContainer/controls\"]\nunique_name_in_owner = true\nuse_parent_material = true\nlayout_mode = 2\ntooltip_text = \"Rerun unit tests (Debug)\"\nshortcut = SubResource(\"Shortcut_ae6em\")\nicon = SubResource(\"ImageTexture_q0wt0\")\n\n[node name=\"stop\" type=\"Button\" parent=\"HBoxContainer/controls\"]\nunique_name_in_owner = true\nuse_parent_material = true\nlayout_mode = 2\ntooltip_text = \"Stops runing unit tests\"\ndisabled = true\nshortcut = SubResource(\"Shortcut_2mb87\")\nicon = SubResource(\"ImageTexture_1wiyx\")\n\n[node name=\"VSeparator4\" type=\"VSeparator\" parent=\"HBoxContainer/controls\"]\nlayout_mode = 2\n\n[node name=\"CenterContainer\" type=\"HBoxContainer\" parent=\"HBoxContainer\"]\nuse_parent_material = true\nlayout_mode = 2\nsize_flags_horizontal = 10\nsize_flags_vertical = 4\nalignment = 2\n\n[node name=\"version\" type=\"Label\" parent=\"HBoxContainer/CenterContainer\"]\nunique_name_in_owner = true\nuse_parent_material = true\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 13\nauto_translate = false\nlocalize_numeral_system = false\ntext = \"gdUnit4 4.3.0\"\nhorizontal_alignment = 1\njustification_flags = 160\n\n[connection signal=\"pressed\" from=\"HBoxContainer/tools/help\" to=\".\" method=\"_on_wiki_pressed\"]\n[connection signal=\"pressed\" from=\"HBoxContainer/tools/tool\" to=\".\" method=\"_on_btn_tool_pressed\"]\n[connection signal=\"pressed\" from=\"HBoxContainer/controls/run_overall\" to=\".\" method=\"_on_runoverall_pressed\"]\n[connection signal=\"pressed\" from=\"HBoxContainer/controls/run\" to=\".\" method=\"_on_run_pressed\"]\n[connection signal=\"pressed\" from=\"HBoxContainer/controls/debug\" to=\".\" method=\"_on_run_pressed\" binds= [true]]\n[connection signal=\"pressed\" from=\"HBoxContainer/controls/stop\" to=\".\" method=\"_on_stop_pressed\"]\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd",
    "content": "@tool\nextends VSplitContainer\n\nsignal run_testcase(test_suite_resource_path: String, test_case: String, test_param_index: int, run_debug: bool)\nsignal run_testsuite()\n\nconst CONTEXT_MENU_RUN_ID = 0\nconst CONTEXT_MENU_DEBUG_ID = 1\nconst CONTEXT_MENU_COLLAPSE_ALL = 3\nconst CONTEXT_MENU_EXPAND_ALL = 4\n\n\n@onready var _tree: Tree = $Panel/Tree\n@onready var _report_list: Node = $report/ScrollContainer/list\n@onready var _report_template: RichTextLabel = $report/report_template\n@onready var _context_menu: PopupMenu = $contextMenu\n@onready var _discover_hint: Control = %discover_hint\n@onready var _spinner: Button = %spinner\n\n# loading tree icons\n@onready var ICON_SPINNER := GdUnitUiTools.get_spinner()\n@onready var ICON_FOLDER := GdUnitUiTools.get_icon(\"Folder\")\n# gdscript icons\n@onready var ICON_GDSCRIPT_TEST_DEFAULT := GdUnitUiTools.get_icon(\"GDScript\", Color.LIGHT_GRAY)\n@onready var ICON_GDSCRIPT_TEST_SUCCESS := GdUnitUiTools.get_GDScript_icon(\"StatusSuccess\", Color.DARK_GREEN)\n@onready var ICON_GDSCRIPT_TEST_FLAKY := GdUnitUiTools.get_GDScript_icon(\"CheckBox\", Color.GREEN_YELLOW)\n@onready var ICON_GDSCRIPT_TEST_FAILED := GdUnitUiTools.get_GDScript_icon(\"StatusError\", Color.SKY_BLUE)\n@onready var ICON_GDSCRIPT_TEST_ERROR := GdUnitUiTools.get_GDScript_icon(\"StatusError\", Color.DARK_RED)\n@onready var ICON_GDSCRIPT_TEST_SUCCESS_ORPHAN := GdUnitUiTools.get_GDScript_icon(\"Unlinked\", Color.DARK_GREEN)\n@onready var ICON_GDSCRIPT_TEST_FAILED_ORPHAN := GdUnitUiTools.get_GDScript_icon(\"Unlinked\", Color.SKY_BLUE)\n@onready var ICON_GDSCRIPT_TEST_ERRORS_ORPHAN := GdUnitUiTools.get_GDScript_icon(\"Unlinked\", Color.DARK_RED)\n# csharp script icons\n@onready var ICON_CSSCRIPT_TEST_DEFAULT := GdUnitUiTools.get_icon(\"CSharpScript\", Color.LIGHT_GRAY)\n@onready var ICON_CSSCRIPT_TEST_SUCCESS := GdUnitUiTools.get_CSharpScript_icon(\"StatusSuccess\", Color.DARK_GREEN)\n@onready var ICON_CSSCRIPT_TEST_FAILED := GdUnitUiTools.get_CSharpScript_icon(\"StatusError\", Color.SKY_BLUE)\n@onready var ICON_CSSCRIPT_TEST_ERROR := GdUnitUiTools.get_CSharpScript_icon(\"StatusError\", Color.DARK_RED)\n@onready var ICON_CSSCRIPT_TEST_SUCCESS_ORPHAN := GdUnitUiTools.get_CSharpScript_icon(\"Unlinked\", Color.DARK_GREEN)\n@onready var ICON_CSSCRIPT_TEST_FAILED_ORPHAN := GdUnitUiTools.get_CSharpScript_icon(\"Unlinked\", Color.SKY_BLUE)\n@onready var ICON_CSSCRIPT_TEST_ERRORS_ORPHAN := GdUnitUiTools.get_CSharpScript_icon(\"Unlinked\", Color.DARK_RED)\n\n\nenum GdUnitType {\n\tFOLDER,\n\tTEST_SUITE,\n\tTEST_CASE,\n\tTEST_CASE_PARAMETERIZED\n}\n\n\nenum STATE {\n\tINITIAL,\n\tRUNNING,\n\tSUCCESS,\n\tWARNING,\n\tFLAKY,\n\tFAILED,\n\tERROR,\n\tABORDED,\n\tSKIPPED\n}\n\nconst META_GDUNIT_ORIGINAL_INDEX = \"gdunit_original_index\"\nconst META_GDUNIT_NAME := \"gdUnit_name\"\nconst META_GDUNIT_STATE := \"gdUnit_state\"\nconst META_GDUNIT_TYPE := \"gdUnit_type\"\nconst META_GDUNIT_TOTAL_TESTS := \"gdUnit_suite_total_tests\"\nconst META_GDUNIT_SUCCESS_TESTS := \"gdUnit_suite_success_tests\"\nconst META_GDUNIT_REPORT := \"gdUnit_report\"\nconst META_GDUNIT_ORPHAN := \"gdUnit_orphan\"\nconst META_GDUNIT_EXECUTION_TIME := \"gdUnit_execution_time\"\nconst META_RESOURCE_PATH := \"resource_path\"\nconst META_LINE_NUMBER := \"line_number\"\nconst META_SCRIPT_PATH := \"script_path\"\nconst META_TEST_PARAM_INDEX := \"test_param_index\"\n\nvar _tree_root: TreeItem\nvar _item_hash := Dictionary()\nvar _tree_view_mode_flat := GdUnitSettings.get_inspector_tree_view_mode() == GdUnitInspectorTreeConstants.TREE_VIEW_MODE.FLAT\n\n\nfunc _build_cache_key(resource_path: String, test_name: String) -> Array:\n\treturn [resource_path, test_name]\n\n\nfunc get_tree_item(resource_path: String, item_name: String) -> TreeItem:\n\tvar key := _build_cache_key(resource_path, item_name)\n\treturn _item_hash.get(key, null)\n\n\nfunc remove_tree_item(resource_path: String, item_name: String) -> bool:\n\tvar key := _build_cache_key(resource_path, item_name)\n\tvar item :TreeItem= _item_hash.get(key, null)\n\tif item:\n\t\titem.get_parent().remove_child(item)\n\t\titem.free()\n\t\treturn _item_hash.erase(key)\n\treturn false\n\n\nfunc add_tree_item_to_cache(resource_path: String, test_name: String, item: TreeItem) -> void:\n\tvar key := _build_cache_key(resource_path, test_name)\n\t_item_hash[key] = item\n\n\nfunc clear_tree_item_cache() -> void:\n\t_item_hash.clear()\n\n\nfunc _find_by_resource_path(current: TreeItem, resource_path: String) -> TreeItem:\n\tfor item in current.get_children():\n\t\tif item.get_meta(META_RESOURCE_PATH) == resource_path:\n\t\t\treturn item\n\treturn null\n\n\nfunc _find_first_item_by_state(parent: TreeItem, item_state: STATE, reverse := false) -> TreeItem:\n\tvar itmes := parent.get_children()\n\tif reverse:\n\t\titmes.reverse()\n\tfor item in itmes:\n\t\tif is_test_case(item) and (is_item_state(item, item_state)):\n\t\t\treturn item\n\t\tvar failure_item := _find_first_item_by_state(item, item_state, reverse)\n\t\tif failure_item != null:\n\t\t\treturn failure_item\n\treturn null\n\n\nfunc _find_last_item_by_state(parent: TreeItem, item_state: STATE) -> TreeItem:\n\treturn _find_first_item_by_state(parent, item_state, true)\n\n\nfunc _find_item_by_state(current: TreeItem, item_state: STATE, prev := false) -> TreeItem:\n\tvar next := current.get_prev_in_tree() if prev else current.get_next_in_tree()\n\tif next == null or next == _tree_root:\n\t\treturn null\n\tif is_test_case(next) and is_item_state(next, item_state):\n\t\treturn next\n\treturn _find_item_by_state(next, item_state, prev)\n\n\nfunc is_item_state(item: TreeItem, item_state: STATE) -> bool:\n\treturn item.has_meta(META_GDUNIT_STATE) and item.get_meta(META_GDUNIT_STATE) == item_state\n\n\nfunc is_state_running(item: TreeItem) -> bool:\n\treturn is_item_state(item, STATE.RUNNING)\n\n\nfunc is_state_success(item: TreeItem) -> bool:\n\treturn is_item_state(item, STATE.SUCCESS)\n\n\nfunc is_state_warning(item: TreeItem) -> bool:\n\treturn is_item_state(item, STATE.WARNING)\n\n\nfunc is_state_failed(item: TreeItem) -> bool:\n\treturn is_item_state(item, STATE.FAILED)\n\n\nfunc is_state_error(item: TreeItem) -> bool:\n\treturn is_item_state(item, STATE.ERROR) or is_item_state(item, STATE.ABORDED)\n\n\nfunc is_item_state_orphan(item: TreeItem) -> bool:\n\treturn item.has_meta(META_GDUNIT_ORPHAN)\n\n\nfunc is_test_suite(item: TreeItem) -> bool:\n\treturn item.has_meta(META_GDUNIT_TYPE) and item.get_meta(META_GDUNIT_TYPE) == GdUnitType.TEST_SUITE\n\n\nfunc is_test_case(item: TreeItem) -> bool:\n\treturn item.has_meta(META_GDUNIT_TYPE) and item.get_meta(META_GDUNIT_TYPE) == GdUnitType.TEST_CASE\n\n\nfunc is_folder(item: TreeItem) -> bool:\n\treturn item.has_meta(META_GDUNIT_TYPE) and item.get_meta(META_GDUNIT_TYPE) == GdUnitType.FOLDER\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc _ready() -> void:\n\t_context_menu.set_item_icon(CONTEXT_MENU_RUN_ID, GdUnitUiTools.get_icon(\"Play\"))\n\t_context_menu.set_item_icon(CONTEXT_MENU_DEBUG_ID, GdUnitUiTools.get_icon(\"PlayStart\"))\n\t_context_menu.set_item_icon(CONTEXT_MENU_EXPAND_ALL, GdUnitUiTools.get_icon(\"ExpandTree\"))\n\t_context_menu.set_item_icon(CONTEXT_MENU_COLLAPSE_ALL, GdUnitUiTools.get_icon(\"CollapseTree\"))\n\t# do colorize the icons\n\t#for index in _context_menu.item_count:\n\t#\t_context_menu.set_item_icon_modulate(index, Color.MEDIUM_PURPLE)\n\n\t_spinner.icon = GdUnitUiTools.get_spinner()\n\tinit_tree()\n\tGdUnitSignals.instance().gdunit_settings_changed.connect(_on_settings_changed)\n\tGdUnitSignals.instance().gdunit_add_test_suite.connect(do_add_test_suite)\n\tGdUnitSignals.instance().gdunit_event.connect(_on_gdunit_event)\n\tvar command_handler := GdUnitCommandHandler.instance()\n\tcommand_handler.gdunit_runner_start.connect(_on_gdunit_runner_start)\n\tcommand_handler.gdunit_runner_stop.connect(_on_gdunit_runner_stop)\n\n\n# we need current to manually redraw bacause of the animation bug\n# https://github.com/godotengine/godot/issues/69330\nfunc _process(_delta: float) -> void:\n\tif is_visible_in_tree():\n\t\tqueue_redraw()\n\n\nfunc init_tree() -> void:\n\tcleanup_tree()\n\t_tree.set_hide_root(true)\n\t_tree.ensure_cursor_is_visible()\n\t_tree.set_allow_reselect(true)\n\t_tree.set_allow_rmb_select(true)\n\t_tree.set_columns(2)\n\t_tree.set_column_clip_content(0, true)\n\t_tree.set_column_expand_ratio(0, 1)\n\t_tree.set_column_custom_minimum_width(0, 240)\n\t_tree.set_column_expand_ratio(1, 0)\n\t_tree.set_column_custom_minimum_width(1, 100)\n\t_tree_root = _tree.create_item()\n\t# fix tree icon scaling\n\tvar scale_factor := EditorInterface.get_editor_scale() if Engine.is_editor_hint() else 1.0\n\t_tree.set(\"theme_override_constants/icon_max_width\", 16 * scale_factor)\n\n\nfunc cleanup_tree() -> void:\n\tclear_reports()\n\tclear_tree_item_cache()\n\tif not _tree_root:\n\t\treturn\n\t_free_recursive()\n\t_tree.clear()\n\n\nfunc _free_recursive(items:=_tree_root.get_children()) -> void:\n\tfor item in items:\n\t\t_free_recursive(item.get_children())\n\t\titem.call_deferred(\"free\")\n\n\nfunc sort_tree_items(parent :TreeItem) -> void:\n\tparent.visible = false\n\tvar items := parent.get_children()\n\n\t# do sort by selected sort mode\n\tmatch GdUnitSettings.get_inspector_tree_sort_mode():\n\t\tGdUnitInspectorTreeConstants.SORT_MODE.UNSORTED:\n\t\t\titems.sort_custom(sort_items_by_original_index)\n\n\t\tGdUnitInspectorTreeConstants.SORT_MODE.NAME_ASCENDING:\n\t\t\titems.sort_custom(sort_items_by_name.bind(true))\n\n\t\tGdUnitInspectorTreeConstants.SORT_MODE.NAME_DESCENDING:\n\t\t\titems.sort_custom(sort_items_by_name.bind(false))\n\n\t\tGdUnitInspectorTreeConstants.SORT_MODE.EXECUTION_TIME:\n\t\t\titems.sort_custom(sort_items_by_execution_time)\n\n\tfor item in items:\n\t\tparent.remove_child(item)\n\t\tparent.add_child(item)\n\t\tif item.get_child_count() > 0:\n\t\t\tsort_tree_items(item)\n\tparent.visible = true\n\t_tree.queue_redraw()\n\n\nfunc sort_items_by_name(a: TreeItem, b: TreeItem, ascending: bool) -> bool:\n\tvar type_a: GdUnitType = a.get_meta(META_GDUNIT_TYPE)\n\tvar type_b: GdUnitType = b.get_meta(META_GDUNIT_TYPE)\n\t # Compare types first\n\tif type_a != type_b:\n\t\treturn type_a == GdUnitType.FOLDER\n\tvar name_a :String = a.get_meta(META_GDUNIT_NAME)\n\tvar name_b :String = b.get_meta(META_GDUNIT_NAME)\n\treturn name_a.naturalnocasecmp_to(name_b) < 0 if ascending else name_a.naturalnocasecmp_to(name_b) > 0\n\n\nfunc sort_items_by_execution_time(a: TreeItem, b: TreeItem) -> bool:\n\tvar type_a: GdUnitType = a.get_meta(META_GDUNIT_TYPE)\n\tvar type_b: GdUnitType = b.get_meta(META_GDUNIT_TYPE)\n\t # Compare types first\n\tif type_a != type_b:\n\t\treturn type_a == GdUnitType.FOLDER\n\tvar execution_time_a :int = a.get_meta(META_GDUNIT_EXECUTION_TIME)\n\tvar execution_time_b :int = b.get_meta(META_GDUNIT_EXECUTION_TIME)\n\t# if has same execution time sort by name\n\tif execution_time_a == execution_time_b:\n\t\tvar name_a :String = a.get_meta(META_GDUNIT_NAME)\n\t\tvar name_b :String = b.get_meta(META_GDUNIT_NAME)\n\t\treturn name_a.naturalnocasecmp_to(name_b) > 0\n\treturn execution_time_a > execution_time_b\n\n\nfunc sort_items_by_original_index(a: TreeItem, b: TreeItem) -> bool:\n\tvar type_a: GdUnitType = a.get_meta(META_GDUNIT_TYPE)\n\tvar type_b: GdUnitType = b.get_meta(META_GDUNIT_TYPE)\n\tif type_a != type_b:\n\t\treturn type_a == GdUnitType.FOLDER\n\tvar index_a :int = a.get_meta(META_GDUNIT_ORIGINAL_INDEX)\n\tvar index_b :int = b.get_meta(META_GDUNIT_ORIGINAL_INDEX)\n\treturn index_a < index_b\n\n\nfunc reset_tree_state(parent: TreeItem) -> void:\n\tfor item in parent.get_children():\n\t\tset_state_initial(item)\n\t\treset_tree_state(item)\n\n\nfunc select_item(item: TreeItem) -> TreeItem:\n\tif item != null:\n\t\t# enshure the parent is collapsed\n\t\tdo_collapse_parent(item)\n\t\titem.select(0)\n\t\t_tree.ensure_cursor_is_visible()\n\t\t_tree.scroll_to_item(item, true)\n\treturn item\n\n\nfunc do_collapse_parent(item: TreeItem) -> void:\n\tif item != null:\n\t\titem.collapsed = false\n\t\tdo_collapse_parent(item.get_parent())\n\n\nfunc do_collapse_all(collapse: bool, parent := _tree_root) -> void:\n\tfor item in parent.get_children():\n\t\titem.collapsed = collapse\n\t\tif not collapse:\n\t\t\tdo_collapse_all(collapse, item)\n\n\nfunc set_state_initial(item: TreeItem) -> void:\n\titem.set_custom_color(0, Color.LIGHT_GRAY)\n\titem.set_tooltip_text(0, \"\")\n\titem.set_text_overrun_behavior(0, TextServer.OVERRUN_TRIM_CHAR)\n\titem.set_expand_right(0, true)\n\n\titem.set_custom_color(1, Color.LIGHT_GRAY)\n\titem.set_text(1, \"\")\n\titem.set_expand_right(1, true)\n\titem.set_tooltip_text(1, \"\")\n\n\titem.set_meta(META_GDUNIT_STATE, STATE.INITIAL)\n\titem.set_meta(META_GDUNIT_SUCCESS_TESTS, 0)\n\titem.remove_meta(META_GDUNIT_REPORT)\n\titem.remove_meta(META_GDUNIT_ORPHAN)\n\tset_item_icon_by_state(item)\n\tinit_item_counter(item)\n\n\nfunc set_state_running(item: TreeItem) -> void:\n\tif is_state_running(item):\n\t\treturn\n\titem.set_custom_color(0, Color.DARK_GREEN)\n\titem.set_custom_color(1, Color.DARK_GREEN)\n\titem.set_icon(0, ICON_SPINNER)\n\titem.set_meta(META_GDUNIT_STATE, STATE.RUNNING)\n\titem.collapsed = false\n\tvar parent := item.get_parent()\n\tif parent != _tree_root:\n\t\tset_state_running(parent)\n\t# force scrolling to current test case\n\t@warning_ignore(\"return_value_discarded\")\n\tselect_item(item)\n\n\nfunc set_state_succeded(item: TreeItem) -> void:\n\titem.set_custom_color(0, Color.GREEN)\n\titem.set_custom_color(1, Color.GREEN)\n\titem.set_meta(META_GDUNIT_STATE, STATE.SUCCESS)\n\titem.collapsed = GdUnitSettings.is_inspector_node_collapse()\n\tset_item_icon_by_state(item)\n\n\nfunc set_state_flaky(item: TreeItem, event: GdUnitEvent) -> void:\n\t# Do not overwrite higher states\n\tif is_state_error(item):\n\t\treturn\n\tvar retry_count := event.statistic(GdUnitEvent.RETRY_COUNT)\n\titem.set_meta(META_GDUNIT_STATE, STATE.FLAKY)\n\tif retry_count > 1:\n\t\titem.set_text(0, \"%s (%s retries)\" % [\n\t\t\titem.get_meta(META_GDUNIT_NAME),\n\t\t\tretry_count])\n\titem.set_custom_color(0, Color.GREEN_YELLOW)\n\titem.set_custom_color(1, Color.GREEN_YELLOW)\n\titem.collapsed = false\n\tset_item_icon_by_state(item)\n\n\nfunc set_state_skipped(item: TreeItem) -> void:\n\titem.set_meta(META_GDUNIT_STATE, STATE.SKIPPED)\n\titem.set_text(1, \"(skipped)\")\n\titem.set_text_alignment(1, HORIZONTAL_ALIGNMENT_RIGHT)\n\titem.set_custom_color(0, Color.DARK_GRAY)\n\titem.set_custom_color(1, Color.DARK_GRAY)\n\titem.collapsed = false\n\tset_item_icon_by_state(item)\n\n\nfunc set_state_warnings(item: TreeItem) -> void:\n\t# Do not overwrite higher states\n\tif is_state_error(item) or is_state_failed(item):\n\t\treturn\n\titem.set_meta(META_GDUNIT_STATE, STATE.WARNING)\n\titem.set_custom_color(0, Color.YELLOW)\n\titem.set_custom_color(1, Color.YELLOW)\n\titem.collapsed = false\n\tset_item_icon_by_state(item)\n\n\nfunc set_state_failed(item: TreeItem, event: GdUnitEvent) -> void:\n\t# Do not overwrite higher states\n\tif is_state_error(item):\n\t\treturn\n\tvar retry_count := event.statistic(GdUnitEvent.RETRY_COUNT)\n\tif retry_count > 1:\n\t\titem.set_text(0, \"%s (%s retries)\" % [\n\t\t\titem.get_meta(META_GDUNIT_NAME),\n\t\t\tretry_count])\n\titem.set_meta(META_GDUNIT_STATE, STATE.FAILED)\n\titem.set_custom_color(0, Color.LIGHT_BLUE)\n\titem.set_custom_color(1, Color.LIGHT_BLUE)\n\titem.collapsed = false\n\tset_item_icon_by_state(item)\n\n\nfunc set_state_error(item: TreeItem) -> void:\n\titem.set_meta(META_GDUNIT_STATE, STATE.ERROR)\n\titem.set_custom_color(0, Color.ORANGE_RED)\n\titem.set_custom_color(1, Color.ORANGE_RED)\n\tset_item_icon_by_state(item)\n\titem.collapsed = false\n\n\nfunc set_state_aborted(item: TreeItem) -> void:\n\titem.set_meta(META_GDUNIT_STATE, STATE.ABORDED)\n\titem.set_custom_color(0, Color.ORANGE_RED)\n\titem.set_custom_color(1, Color.ORANGE_RED)\n\titem.clear_custom_bg_color(0)\n\titem.set_text(1, \"(aborted)\")\n\titem.set_text_alignment(1, HORIZONTAL_ALIGNMENT_RIGHT)\n\tset_item_icon_by_state(item)\n\titem.collapsed = false\n\n\nfunc set_state_orphan(item: TreeItem, event: GdUnitEvent) -> void:\n\tvar orphan_count := event.statistic(GdUnitEvent.ORPHAN_NODES)\n\tif orphan_count == 0:\n\t\treturn\n\tif item.has_meta(META_GDUNIT_ORPHAN):\n\t\torphan_count += item.get_meta(META_GDUNIT_ORPHAN)\n\titem.set_meta(META_GDUNIT_ORPHAN, orphan_count)\n\tif item.get_meta(META_GDUNIT_STATE) != STATE.FAILED:\n\t\titem.set_custom_color(0, Color.YELLOW)\n\t\titem.set_custom_color(1, Color.YELLOW)\n\titem.set_tooltip_text(0, \"Total <%d> orphan nodes detected.\" % orphan_count)\n\tset_item_icon_by_state(item)\n\n\nfunc update_state(item: TreeItem, event: GdUnitEvent, add_reports := true) -> void:\n\t# we do not show the root\n\tif item == _tree_root:\n\t\treturn\n\n\tif event.is_success() and event.is_flaky():\n\t\tset_state_flaky(item, event)\n\telif event.is_success():\n\t\tset_state_succeded(item)\n\telif event.is_skipped():\n\t\tset_state_skipped(item)\n\telif event.is_error():\n\t\tset_state_error(item)\n\telif event.is_failed():\n\t\tset_state_failed(item, event)\n\telif event.is_warning():\n\t\tset_state_warnings(item)\n\tif add_reports:\n\t\tfor report in event.reports():\n\t\t\tadd_report(item, report)\n\tset_state_orphan(item, event)\n\tif is_folder(item):\n\t\tupdate_state(item.get_parent(), event, false)\n\n\nfunc add_report(item: TreeItem, report: GdUnitReport) -> void:\n\tvar reports: Array[GdUnitReport] = []\n\tif item.has_meta(META_GDUNIT_REPORT):\n\t\treports = get_item_reports(item)\n\treports.append(report)\n\titem.set_meta(META_GDUNIT_REPORT, reports)\n\n\nfunc abort_running(items:=_tree_root.get_children()) -> void:\n\tfor item in items:\n\t\tif is_state_running(item):\n\t\t\tset_state_aborted(item)\n\t\t\tabort_running(item.get_children())\n\n\nfunc select_first_failure() -> TreeItem:\n\treturn select_item(_find_first_item_by_state(_tree_root, STATE.FAILED))\n\n\nfunc _on_select_next_item_by_state(item_state: int) -> TreeItem:\n\tvar current_selected := _tree.get_selected()\n\t# If nothing is selected, the first error is selected or the next one in the vicinity of the current selection is found\n\tcurrent_selected = _find_first_item_by_state(_tree_root, item_state) if current_selected == null else _find_item_by_state(current_selected, item_state)\n\t# If no next failure found, then we try to select first\n\tif current_selected == null:\n\t\tcurrent_selected = _find_first_item_by_state(_tree_root, item_state)\n\treturn select_item(current_selected)\n\n\nfunc _on_select_previous_item_by_state(item_state: int) -> TreeItem:\n\tvar current_selected := _tree.get_selected()\n\t# If nothing is selected, the first error is selected or the next one in the vicinity of the current selection is found\n\tcurrent_selected = _find_last_item_by_state(_tree_root, item_state) if current_selected == null else _find_item_by_state(current_selected, item_state, true)\n\t# If no next failure found, then we try to select first last\n\tif current_selected == null:\n\t\tcurrent_selected = _find_last_item_by_state(_tree_root, item_state)\n\treturn select_item(current_selected)\n\n\nfunc select_first_orphan() -> void:\n\tfor parent in _tree_root.get_children():\n\t\tif not is_state_success(parent):\n\t\t\tfor item in parent.get_children():\n\t\t\t\tif is_item_state_orphan(item):\n\t\t\t\t\tparent.set_collapsed(false)\n\t\t\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t\t\tselect_item(item)\n\t\t\t\t\treturn\n\n\nfunc clear_reports() -> void:\n\tfor child in _report_list.get_children():\n\t\t_report_list.remove_child(child)\n\t\tchild.queue_free()\n\n\nfunc show_failed_report(selected_item: TreeItem) -> void:\n\tclear_reports()\n\tif selected_item == null or not selected_item.has_meta(META_GDUNIT_REPORT):\n\t\treturn\n\t# add new reports\n\tfor report in get_item_reports(selected_item):\n\t\tvar reportNode: RichTextLabel = _report_template.duplicate()\n\t\t_report_list.add_child(reportNode)\n\t\treportNode.append_text(report.to_string())\n\t\treportNode.visible = true\n\n\nfunc update_test_suite(event: GdUnitEvent) -> void:\n\tvar item := get_tree_item(extract_resource_path(event), event.suite_name())\n\tif not item:\n\t\tpush_error(\"Internal Error: Can't find test suite %s\" % event.suite_name())\n\t\treturn\n\tif event.type() == GdUnitEvent.TESTSUITE_BEFORE:\n\t\tset_state_running(item)\n\t\treturn\n\tif event.type() == GdUnitEvent.TESTSUITE_AFTER:\n\t\tupdate_item_counter(item)\n\t\tupdate_item_elapsed_time_counter(item, event.elapsed_time())\n\n\tupdate_state(item, event)\n\tupdate_state(item.get_parent(), event, false)\n\n\nfunc update_test_case(event: GdUnitEvent) -> void:\n\tvar item := get_tree_item(extract_resource_path(event), event.test_name())\n\tif not item:\n\t\tpush_error(\"Internal Error: Can't find test case %s:%s\" % [event.suite_name(), event.test_name()])\n\t\treturn\n\tif event.type() == GdUnitEvent.TESTCASE_BEFORE:\n\t\tset_state_running(item)\n\t\treturn\n\tif event.type() == GdUnitEvent.TESTCASE_AFTER:\n\t\tupdate_item_elapsed_time_counter(item, event.elapsed_time())\n\t\tif event.is_success() or event.is_warning():\n\t\t\tupdate_item_counter(item)\n\tupdate_state(item, event)\n\n\nfunc create_tree_item(test_suite: GdUnitTestSuiteDto) -> TreeItem:\n\tvar parent := _tree_root\n\tvar test_root_folder := GdUnitSettings.test_root_folder()\n\tvar resource_path := ProjectSettings.localize_path(test_suite.path())\n\tvar test_base_path := \"res://\"\n\tvar test_relative_path := resource_path\n\tif resource_path.contains(test_root_folder):\n\t\tvar path_elements := resource_path.split(test_root_folder)\n\t\ttest_base_path = path_elements[0] + \"/\" + test_root_folder\n\t\ttest_relative_path = path_elements[1]\n\ttest_relative_path = test_relative_path.replace(\"res://\", \"\")\n\n\tif _tree_view_mode_flat:\n\t\tvar element := test_relative_path.get_base_dir().trim_prefix(\"/\")\n\t\tif element.is_empty():\n\t\t\treturn _tree.create_item(parent)\n\t\ttest_base_path += \"/\" + element\n\t\tparent = create_or_find_item(parent, test_base_path, element)\n\t\treturn _tree.create_item(parent)\n\n\tvar elements := test_relative_path.split(\"/\")\n\tif elements[0] == \"res://\" or elements[0] == \"\":\n\t\telements.remove_at(0)\n\tif elements.size() > 0:\n\t\telements.remove_at(elements.size() - 1)\n\tfor element in elements:\n\t\ttest_base_path += \"/\" + element\n\t\tparent = create_or_find_item(parent, test_base_path, element)\n\treturn _tree.create_item(parent)\n\n\nfunc create_or_find_item(parent: TreeItem, resource_path: String, item_name: String) -> TreeItem:\n\tvar item := _find_by_resource_path(parent, resource_path)\n\tif item != null:\n\t\treturn item\n\titem = _tree.create_item(parent)\n\titem.set_meta(META_GDUNIT_ORIGINAL_INDEX, item.get_index())\n\titem.set_text(0, item_name)\n\titem.set_meta(META_GDUNIT_STATE, STATE.INITIAL)\n\titem.set_meta(META_GDUNIT_NAME, item_name)\n\titem.set_meta(META_GDUNIT_TYPE, GdUnitType.FOLDER)\n\titem.set_meta(META_RESOURCE_PATH, resource_path)\n\titem.set_meta(META_GDUNIT_TOTAL_TESTS, 0)\n\titem.set_meta(META_GDUNIT_EXECUTION_TIME, 0)\n\tset_item_icon_by_state(item)\n\titem.collapsed = true\n\treturn item\n\n\nfunc create_item(parent: TreeItem, resource_path: String, item_name: String, type: GdUnitType) -> TreeItem:\n\tvar item := _tree.create_item(parent)\n\titem.set_meta(META_GDUNIT_ORIGINAL_INDEX, item.get_index())\n\titem.set_text(0, item_name)\n\titem.set_meta(META_GDUNIT_STATE, STATE.INITIAL)\n\titem.set_meta(META_GDUNIT_NAME, item_name)\n\titem.set_meta(META_GDUNIT_TYPE, type)\n\titem.set_meta(META_RESOURCE_PATH, resource_path)\n\titem.set_meta(META_GDUNIT_TOTAL_TESTS, 0)\n\titem.set_meta(META_GDUNIT_EXECUTION_TIME, 0)\n\tset_item_icon_by_state(item)\n\titem.collapsed = true\n\treturn item\n\n\nfunc set_item_icon_by_state(item :TreeItem) -> void:\n\tvar resource_path :String = item.get_meta(META_RESOURCE_PATH)\n\tvar state :STATE = item.get_meta(META_GDUNIT_STATE)\n\tvar is_orphan := is_item_state_orphan(item)\n\titem.set_icon(0, get_icon_by_file_type(resource_path, state, is_orphan))\n\tif item.get_meta(META_GDUNIT_TYPE) == GdUnitType.FOLDER:\n\t\titem.set_icon_modulate(0, Color.SKY_BLUE)\n\n\nfunc init_item_counter(item: TreeItem) -> void:\n\tif item.has_meta(META_GDUNIT_TOTAL_TESTS) and item.get_meta(META_GDUNIT_TOTAL_TESTS) > 0:\n\t\titem.set_text(0, \"(0/%s) %s\" % [\n\t\t\titem.get_meta(META_GDUNIT_TOTAL_TESTS),\n\t\t\titem.get_meta(META_GDUNIT_NAME)])\n\tinit_folder_counter(item.get_parent())\n\n\nfunc increment_item_counter(item: TreeItem, increment_count: int) -> void:\n\tif item != _tree_root and item.get_meta(META_GDUNIT_TOTAL_TESTS) != 0:\n\t\tvar count: int = item.get_meta(META_GDUNIT_SUCCESS_TESTS)\n\t\titem.set_meta(META_GDUNIT_SUCCESS_TESTS, count + increment_count)\n\t\titem.set_text(0, \"(%s/%s) %s\" % [\n\t\t\titem.get_meta(META_GDUNIT_SUCCESS_TESTS),\n\t\t\titem.get_meta(META_GDUNIT_TOTAL_TESTS),\n\t\t\titem.get_meta(META_GDUNIT_NAME)])\n\t\tif is_folder(item):\n\t\t\tincrement_item_counter(item.get_parent(), increment_count)\n\n\nfunc init_folder_counter(item: TreeItem) -> void:\n\tif item == _tree_root:\n\t\treturn\n\tvar type :GdUnitType = item.get_meta(META_GDUNIT_TYPE)\n\tif type == GdUnitType.FOLDER:\n\t\tvar count :int = item.get_children().reduce(count_tests_total, 0)\n\t\titem.set_meta(META_GDUNIT_TOTAL_TESTS, count)\n\t\titem.set_meta(META_GDUNIT_SUCCESS_TESTS, 0)\n\t\titem.set_meta(META_GDUNIT_EXECUTION_TIME, 0)\n\t\tinit_item_counter(item)\n\n\nfunc count_tests_total(accum: int, item: TreeItem) -> int:\n\treturn accum + item.get_meta(META_GDUNIT_TOTAL_TESTS)\n\n\nfunc update_item_counter(item: TreeItem) -> void:\n\tif item == _tree_root:\n\t\treturn\n\tvar type :GdUnitType = item.get_meta(META_GDUNIT_TYPE)\n\tmatch type:\n\t\tGdUnitType.TEST_CASE:\n\t\t\tincrement_item_counter(item.get_parent(), 1)\n\t\tGdUnitType.TEST_CASE_PARAMETERIZED:\n\t\t\tincrement_item_counter(item.get_parent(), 1)\n\t\tGdUnitType.TEST_SUITE:\n\t\t\tvar count: int = item.get_meta(META_GDUNIT_SUCCESS_TESTS)\n\t\t\tincrement_item_counter(item.get_parent(), count)\n\n\nfunc update_item_elapsed_time_counter(item: TreeItem, time: int) -> void:\n\titem.set_text(1, \"%s\" % LocalTime.elapsed(time))\n\titem.set_text_alignment(1, HORIZONTAL_ALIGNMENT_RIGHT)\n\titem.set_meta(META_GDUNIT_EXECUTION_TIME, time)\n\n\tvar parent := item.get_parent()\n\tif parent == _tree_root:\n\t\treturn\n\tvar elapsed_time :int = parent.get_meta(META_GDUNIT_EXECUTION_TIME) + time\n\tvar type :GdUnitType = item.get_meta(META_GDUNIT_TYPE)\n\tmatch type:\n\t\tGdUnitType.TEST_CASE:\n\t\t\treturn\n\t\tGdUnitType.TEST_SUITE:\n\t\t\tupdate_item_elapsed_time_counter(parent, elapsed_time)\n\t\t#GdUnitType.FOLDER:\n\t\t#\tupdate_item_elapsed_time_counter(parent, elapsed_time)\n\n\nfunc get_icon_by_file_type(path: String, state: STATE, orphans: bool) -> Texture2D:\n\tif path.get_extension() == \"gd\":\n\t\tmatch state:\n\t\t\tSTATE.INITIAL:\n\t\t\t\treturn ICON_GDSCRIPT_TEST_DEFAULT\n\t\t\tSTATE.SUCCESS:\n\t\t\t\treturn ICON_GDSCRIPT_TEST_SUCCESS_ORPHAN if orphans else ICON_GDSCRIPT_TEST_SUCCESS\n\t\t\tSTATE.ERROR:\n\t\t\t\treturn ICON_GDSCRIPT_TEST_ERRORS_ORPHAN if orphans else ICON_GDSCRIPT_TEST_ERROR\n\t\t\tSTATE.FAILED:\n\t\t\t\treturn ICON_GDSCRIPT_TEST_FAILED_ORPHAN if orphans else ICON_GDSCRIPT_TEST_FAILED\n\t\t\tSTATE.WARNING:\n\t\t\t\treturn ICON_GDSCRIPT_TEST_SUCCESS_ORPHAN if orphans else ICON_GDSCRIPT_TEST_DEFAULT\n\t\t\tSTATE.FLAKY:\n\t\t\t\treturn ICON_GDSCRIPT_TEST_SUCCESS_ORPHAN if orphans else ICON_GDSCRIPT_TEST_FLAKY\n\t\t\t_:\n\t\t\t\treturn ICON_GDSCRIPT_TEST_DEFAULT\n\tif path.get_extension() == \"cs\":\n\t\tmatch state:\n\t\t\tSTATE.INITIAL:\n\t\t\t\treturn ICON_CSSCRIPT_TEST_DEFAULT\n\t\t\tSTATE.SUCCESS:\n\t\t\t\treturn ICON_CSSCRIPT_TEST_SUCCESS_ORPHAN if orphans else ICON_CSSCRIPT_TEST_SUCCESS\n\t\t\tSTATE.ERROR:\n\t\t\t\treturn ICON_CSSCRIPT_TEST_ERRORS_ORPHAN if orphans else ICON_CSSCRIPT_TEST_ERROR\n\t\t\tSTATE.FAILED:\n\t\t\t\treturn ICON_CSSCRIPT_TEST_FAILED_ORPHAN if orphans else ICON_CSSCRIPT_TEST_FAILED\n\t\t\tSTATE.WARNING:\n\t\t\t\treturn ICON_CSSCRIPT_TEST_SUCCESS_ORPHAN if orphans else ICON_CSSCRIPT_TEST_DEFAULT\n\t\t\t_:\n\t\t\t\treturn ICON_CSSCRIPT_TEST_DEFAULT\n\tmatch state:\n\t\tSTATE.INITIAL:\n\t\t\treturn ICON_FOLDER\n\t\tSTATE.ERROR:\n\t\t\treturn ICON_FOLDER\n\t\tSTATE.FAILED:\n\t\t\treturn ICON_FOLDER\n\t\t_:\n\t\t\treturn ICON_FOLDER\n\n\nfunc discover_test_suite_added(event: GdUnitEventTestDiscoverTestSuiteAdded) -> void:\n\t# Check first if the test suite already exists\n\tvar item := get_tree_item(extract_resource_path(event), event.suite_name())\n\tif item != null:\n\t\treturn\n\t# Otherwise create it\n\tprints(\"Discovered test suite added: '%s' on %s\" % [event.suite_name(), extract_resource_path(event)])\n\tdo_add_test_suite(event.suite_dto())\n\n\nfunc discover_test_added(event: GdUnitEventTestDiscoverTestAdded) -> void:\n\t# check if the test already exists\n\tvar test_name := event.test_case_dto().name()\n\tvar resource_path := extract_resource_path(event)\n\tvar item := get_tree_item(resource_path, test_name)\n\tif item != null:\n\t\treturn\n\n\titem = get_tree_item(resource_path, event.suite_name())\n\tif not item:\n\t\tpush_error(\"Internal Error: Can't find test suite %s:%s\" % [event.suite_name(), resource_path])\n\t\treturn\n\tprints(\"Discovered test added: '%s' on %s\" % [event.test_name(), resource_path])\n\t# update test case count\n\tvar test_count :int = item.get_meta(META_GDUNIT_TOTAL_TESTS)\n\titem.set_meta(META_GDUNIT_TOTAL_TESTS, test_count + 1)\n\tinit_item_counter(item)\n\t# add new discovered test\n\tadd_test(item, event.test_case_dto())\n\n\nfunc discover_test_removed(event: GdUnitEventTestDiscoverTestRemoved) -> void:\n\tvar resource_path := extract_resource_path(event)\n\tprints(\"Discovered test removed: '%s' on %s\" % [event.test_name(), resource_path])\n\tvar item := get_tree_item(resource_path, event.test_name())\n\tif not item:\n\t\tpush_error(\"Internal Error: Can't find test suite %s:%s\" % [event.suite_name(), resource_path])\n\t\treturn\n\t# update test case count on test suite\n\tvar parent := item.get_parent()\n\tvar test_count :int = parent.get_meta(META_GDUNIT_TOTAL_TESTS)\n\tparent.set_meta(META_GDUNIT_TOTAL_TESTS, test_count - 1)\n\tinit_item_counter(parent)\n\t# finally remove the test\n\t@warning_ignore(\"return_value_discarded\")\n\tremove_tree_item(resource_path, event.test_name())\n\n\nfunc do_add_test_suite(test_suite: GdUnitTestSuiteDto) -> void:\n\tvar item := create_tree_item(test_suite)\n\tvar suite_name := test_suite.name()\n\tvar resource_path := ProjectSettings.localize_path(test_suite.path())\n\titem.set_text(0, suite_name)\n\titem.set_meta(META_GDUNIT_ORIGINAL_INDEX, item.get_index())\n\titem.set_meta(META_GDUNIT_STATE, STATE.INITIAL)\n\titem.set_meta(META_GDUNIT_NAME, suite_name)\n\titem.set_meta(META_GDUNIT_TYPE, GdUnitType.TEST_SUITE)\n\titem.set_meta(META_GDUNIT_TOTAL_TESTS, test_suite.test_case_count())\n\titem.set_meta(META_GDUNIT_SUCCESS_TESTS, 0)\n\titem.set_meta(META_GDUNIT_EXECUTION_TIME, 0)\n\titem.set_meta(META_RESOURCE_PATH, resource_path)\n\titem.set_meta(META_LINE_NUMBER, 1)\n\titem.collapsed = true\n\tset_item_icon_by_state(item)\n\tinit_item_counter(item)\n\tadd_tree_item_to_cache(resource_path, suite_name, item)\n\tfor test_case in test_suite.test_cases():\n\t\tadd_test(item, test_case)\n\n\nfunc add_test(parent: TreeItem, test_case: GdUnitTestCaseDto) -> void:\n\tvar item := _tree.create_item(parent)\n\tvar test_name := test_case.name()\n\tvar resource_path :String = parent.get_meta(META_RESOURCE_PATH)\n\tvar test_case_names := test_case.test_case_names()\n\n\titem.set_meta(META_GDUNIT_ORIGINAL_INDEX, item.get_index())\n\titem.set_text(0, test_name)\n\titem.set_meta(META_GDUNIT_STATE, STATE.INITIAL)\n\titem.set_meta(META_GDUNIT_NAME, test_name)\n\titem.set_meta(META_GDUNIT_TYPE, GdUnitType.TEST_CASE)\n\titem.set_meta(META_RESOURCE_PATH, resource_path)\n\titem.set_meta(META_GDUNIT_SUCCESS_TESTS, 0)\n\titem.set_meta(META_GDUNIT_EXECUTION_TIME, 0)\n\titem.set_meta(META_GDUNIT_TOTAL_TESTS, test_case_names.size())\n\titem.set_meta(META_SCRIPT_PATH, test_case.script_path())\n\titem.set_meta(META_LINE_NUMBER, test_case.line_number())\n\titem.set_meta(META_TEST_PARAM_INDEX, -1)\n\tset_item_icon_by_state(item)\n\tinit_item_counter(item)\n\tadd_tree_item_to_cache(resource_path, test_name, item)\n\tif not test_case_names.is_empty():\n\t\tadd_test_cases(item, test_case_names)\n\n\nfunc add_test_cases(parent: TreeItem, test_case_names: PackedStringArray) -> void:\n\tfor index in test_case_names.size():\n\t\tvar item := _tree.create_item(parent)\n\t\tvar test_case_name := test_case_names[index]\n\t\tvar resource_path :String = parent.get_meta(META_RESOURCE_PATH)\n\t\titem.set_meta(META_GDUNIT_ORIGINAL_INDEX, item.get_index())\n\t\titem.set_text(0, test_case_name)\n\t\titem.set_meta(META_GDUNIT_STATE, STATE.INITIAL)\n\t\titem.set_meta(META_GDUNIT_NAME, test_case_name)\n\t\titem.set_meta(META_GDUNIT_TOTAL_TESTS, 0)\n\t\titem.set_meta(META_GDUNIT_TYPE, GdUnitType.TEST_CASE_PARAMETERIZED)\n\t\titem.set_meta(META_GDUNIT_EXECUTION_TIME, 0)\n\t\titem.set_meta(META_RESOURCE_PATH, resource_path)\n\t\titem.set_meta(META_SCRIPT_PATH, parent.get_meta(META_SCRIPT_PATH))\n\t\titem.set_meta(META_LINE_NUMBER, parent.get_meta(META_LINE_NUMBER))\n\t\titem.set_meta(META_TEST_PARAM_INDEX, index)\n\t\tset_item_icon_by_state(item)\n\t\tadd_tree_item_to_cache(resource_path, test_case_name, item)\n\n\nfunc get_item_reports(item: TreeItem) -> Array[GdUnitReport]:\n\treturn item.get_meta(META_GDUNIT_REPORT)\n\n\nfunc _dump_tree_as_json(dump_name: String) -> void:\n\tvar dict := _to_json(_tree_root)\n\tvar file := FileAccess.open(\"res://%s.json\" % dump_name, FileAccess.WRITE)\n\tfile.store_string(JSON.stringify(dict, \"\\t\"))\n\n\nfunc _to_json(parent :TreeItem) -> Dictionary:\n\tvar item_as_dict := GdObjects.obj2dict(parent)\n\titem_as_dict[\"TreeItem\"][\"childs\"] = parent.get_children().map(func(item: TreeItem) -> Dictionary:\n\t\t\treturn _to_json(item))\n\treturn item_as_dict\n\n\nfunc extract_resource_path(event: GdUnitEvent) -> String:\n\treturn ProjectSettings.localize_path(event.resource_path())\n\n\n################################################################################\n# Tree signal receiver\n################################################################################\nfunc _on_tree_item_mouse_selected(mouse_position: Vector2, mouse_button_index: int) -> void:\n\tif mouse_button_index == MOUSE_BUTTON_RIGHT:\n\t\t_context_menu.position = get_screen_position() + mouse_position\n\t\t_context_menu.popup()\n\n\nfunc _on_run_pressed(run_debug: bool) -> void:\n\t_context_menu.hide()\n\tvar item: = _tree.get_selected()\n\tif item == null:\n\t\tprint_rich(\"[color=GOLDENROD]Abort Testrun, no test suite selected![/color]\")\n\t\treturn\n\tif item.get_meta(META_GDUNIT_TYPE) == GdUnitType.TEST_SUITE or item.get_meta(META_GDUNIT_TYPE) == GdUnitType.FOLDER:\n\t\tvar resource_path: String = item.get_meta(META_RESOURCE_PATH)\n\t\trun_testsuite.emit([resource_path], run_debug)\n\t\treturn\n\tvar parent := item.get_parent()\n\tvar test_suite_resource_path: String = parent.get_meta(META_RESOURCE_PATH)\n\tvar test_case: String = item.get_meta(META_GDUNIT_NAME)\n\t# handle parameterized test selection\n\tvar test_param_index: int = item.get_meta(META_TEST_PARAM_INDEX)\n\tif test_param_index != -1:\n\t\ttest_case = parent.get_meta(META_GDUNIT_NAME)\n\trun_testcase.emit(test_suite_resource_path, test_case, test_param_index, run_debug)\n\n\nfunc _on_Tree_item_selected() -> void:\n\t# only show report checked manual item selection\n\t# we need to check the run mode here otherwise it will be called every selection\n\tif not _context_menu.is_item_disabled(CONTEXT_MENU_RUN_ID):\n\t\tvar selected_item: TreeItem = _tree.get_selected()\n\t\tshow_failed_report(selected_item)\n\n\n# Opens the test suite\nfunc _on_Tree_item_activated() -> void:\n\tvar selected_item := _tree.get_selected()\n\tif selected_item != null and selected_item.has_meta(META_LINE_NUMBER):\n\t\tvar script_path: String = (\n\t\t\tselected_item.get_meta(META_RESOURCE_PATH) if is_test_suite(selected_item)\n\t\t\telse selected_item.get_meta(META_SCRIPT_PATH)\n\t\t)\n\t\tvar line_number: int = selected_item.get_meta(META_LINE_NUMBER)\n\t\tvar resource: Script = load(script_path)\n\n\t\tif selected_item.has_meta(META_GDUNIT_REPORT):\n\t\t\tvar reports := get_item_reports(selected_item)\n\t\t\tvar report_line_number := reports[0].line_number()\n\t\t\t# if number -1 we use original stored line number of the test case\n\t\t\t# in non debug mode the line number is not available\n\t\t\tif report_line_number != -1:\n\t\t\t\tline_number = report_line_number\n\n\t\tEditorInterface.get_file_system_dock().navigate_to_path(script_path)\n\t\tEditorInterface.edit_script(resource, line_number)\n\telif selected_item.get_meta(META_GDUNIT_TYPE) == GdUnitType.FOLDER:\n\t\t# Toggle collapse if dir\n\t\tselected_item.collapsed = not selected_item.collapsed\n\n\n################################################################################\n# external signal receiver\n################################################################################\nfunc _on_gdunit_runner_start() -> void:\n\treset_tree_state(_tree_root)\n\t_context_menu.set_item_disabled(CONTEXT_MENU_RUN_ID, true)\n\t_context_menu.set_item_disabled(CONTEXT_MENU_DEBUG_ID, true)\n\tclear_reports()\n\n\nfunc _on_gdunit_runner_stop(_client_id: int) -> void:\n\t_context_menu.set_item_disabled(CONTEXT_MENU_RUN_ID, false)\n\t_context_menu.set_item_disabled(CONTEXT_MENU_DEBUG_ID, false)\n\tabort_running()\n\tsort_tree_items(_tree_root)\n\t# wait until the tree redraw\n\tawait get_tree().process_frame\n\t@warning_ignore(\"return_value_discarded\")\n\tselect_first_failure()\n\n\nfunc _on_gdunit_event(event: GdUnitEvent) -> void:\n\tmatch event.type():\n\t\tGdUnitEvent.DISCOVER_START:\n\t\t\t_tree_root.visible = false\n\t\t\t_discover_hint.visible = true\n\t\t\tinit_tree()\n\n\t\tGdUnitEvent.DISCOVER_END:\n\t\t\tsort_tree_items(_tree_root)\n\t\t\t_discover_hint.visible = false\n\t\t\t_tree_root.visible = true\n\t\t\t#_dump_tree_as_json(\"tree_example_discovered\")\n\n\t\tGdUnitEvent.DISCOVER_SUITE_ADDED:\n\t\t\tdiscover_test_suite_added(event as GdUnitEventTestDiscoverTestSuiteAdded)\n\n\t\tGdUnitEvent.DISCOVER_TEST_ADDED:\n\t\t\tdiscover_test_added(event as GdUnitEventTestDiscoverTestAdded)\n\n\t\tGdUnitEvent.DISCOVER_TEST_REMOVED:\n\t\t\tdiscover_test_removed(event as GdUnitEventTestDiscoverTestRemoved)\n\n\t\tGdUnitEvent.INIT:\n\t\t\tif not GdUnitSettings.is_test_discover_enabled():\n\t\t\t\tinit_tree()\n\n\t\tGdUnitEvent.STOP:\n\t\t\tsort_tree_items(_tree_root)\n\t\t\t#_dump_tree_as_json(\"tree_example\")\n\n\t\tGdUnitEvent.TESTCASE_BEFORE:\n\t\t\tupdate_test_case(event)\n\n\t\tGdUnitEvent.TESTCASE_AFTER:\n\t\t\tupdate_test_case(event)\n\n\t\tGdUnitEvent.TESTSUITE_BEFORE:\n\t\t\tupdate_test_suite(event)\n\n\t\tGdUnitEvent.TESTSUITE_AFTER:\n\t\t\tupdate_test_suite(event)\n\n\nfunc _on_context_m_index_pressed(index: int) -> void:\n\tmatch index:\n\t\tCONTEXT_MENU_DEBUG_ID:\n\t\t\t_on_run_pressed(true)\n\t\tCONTEXT_MENU_RUN_ID:\n\t\t\t_on_run_pressed(false)\n\t\tCONTEXT_MENU_EXPAND_ALL:\n\t\t\tdo_collapse_all(false)\n\t\tCONTEXT_MENU_COLLAPSE_ALL:\n\t\t\tdo_collapse_all(true)\n\n\nfunc _on_settings_changed(property :GdUnitProperty) -> void:\n\tif property.name() == GdUnitSettings.INSPECTOR_TREE_SORT_MODE:\n\t\tsort_tree_items(_tree_root)\n\t\t# _dump_tree_as_json(\"tree_sorted_by_%s\" % GdUnitInspectorTreeConstants.SORT_MODE.keys()[property.value()])\n\n\tif property.name() == GdUnitSettings.INSPECTOR_TREE_VIEW_MODE:\n\t\t_tree_view_mode_flat = property.value() == GdUnitInspectorTreeConstants.TREE_VIEW_MODE.FLAT\n\t\tGdUnitCommandHandler.instance().cmd_discover_tests()\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd.uid",
    "content": "uid://cutla0p0blhtp\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/parts/InspectorTreePanel.tscn",
    "content": "[gd_scene load_steps=27 format=3 uid=\"uid://bqfpidewtpeg0\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd\" id=\"1\"]\n\n[sub_resource type=\"Image\" id=\"Image_jitv6\"]\ndata = {\n\"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, 4, 226, 226, 226, 26, 224, 224, 224, 41, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 223, 224, 224, 224, 148, 228, 228, 228, 28, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 211, 255, 255, 255, 5, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 221, 229, 229, 229, 29, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 226, 226, 226, 43, 227, 227, 227, 9, 255, 255, 255, 0, 227, 227, 227, 9, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 65, 229, 229, 229, 29, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 192, 192, 192, 4, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 224, 224, 224, 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, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 1, 255, 255, 255, 1, 255, 255, 255, 1, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 229, 229, 229, 39, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 255, 255, 255, 8, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 192, 192, 192, 4, 226, 226, 226, 43, 224, 224, 224, 8, 255, 255, 255, 0, 227, 227, 227, 9, 227, 227, 227, 18, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 227, 227, 227, 9, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 255, 255, 255, 3, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 1, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 1, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 1, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 192, 192, 192, 4, 226, 226, 226, 26, 224, 224, 224, 40, 255, 255, 255, 0, 255, 255, 255, 1, 229, 229, 229, 39, 225, 225, 225, 25, 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),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_nwpuj\"]\nimage = SubResource(\"Image_jitv6\")\n\n[sub_resource type=\"Image\" id=\"Image_ww2bl\"]\ndata = {\n\"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, 4, 226, 226, 226, 26, 224, 224, 224, 41, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 41, 226, 226, 226, 26, 192, 192, 192, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 1, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 223, 223, 223, 47, 224, 224, 224, 8, 255, 255, 255, 0, 224, 224, 224, 8, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 226, 226, 226, 43, 227, 227, 227, 9, 255, 255, 255, 0, 227, 227, 227, 9, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 17, 255, 255, 255, 8, 255, 255, 255, 0, 224, 224, 224, 48, 224, 224, 224, 217, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 228, 228, 228, 47, 224, 224, 224, 236, 224, 224, 224, 255, 225, 225, 225, 125, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 86, 224, 224, 224, 252, 224, 224, 224, 252, 224, 224, 224, 194, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 230, 230, 230, 40, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 255, 255, 255, 8, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 192, 192, 192, 4, 226, 226, 226, 43, 224, 224, 224, 8, 255, 255, 255, 0, 227, 227, 227, 9, 227, 227, 227, 18, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 227, 227, 227, 9, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 255, 255, 255, 3, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 1, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 192, 192, 192, 4, 225, 225, 225, 25, 230, 230, 230, 40, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 40, 225, 225, 225, 25, 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),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_pdcj5\"]\nimage = SubResource(\"Image_ww2bl\")\n\n[sub_resource type=\"Image\" id=\"Image_xkapw\"]\ndata = {\n\"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, 4, 226, 226, 226, 26, 224, 224, 224, 41, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 41, 226, 226, 226, 26, 192, 192, 192, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 1, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 223, 223, 223, 47, 224, 224, 224, 8, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 226, 226, 226, 43, 227, 227, 227, 9, 255, 255, 255, 0, 227, 227, 227, 9, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 17, 255, 255, 255, 8, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 192, 192, 192, 4, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 224, 224, 224, 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, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 89, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 200, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 255, 255, 255, 8, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 42, 224, 224, 224, 233, 224, 224, 224, 255, 225, 225, 225, 124, 255, 255, 255, 0, 255, 255, 255, 0, 192, 192, 192, 4, 226, 226, 226, 43, 224, 224, 224, 8, 255, 255, 255, 0, 227, 227, 227, 9, 227, 227, 227, 18, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 227, 227, 227, 9, 255, 255, 255, 0, 225, 225, 225, 42, 224, 224, 224, 211, 238, 238, 238, 15, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 1, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 1, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 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, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 1, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 192, 192, 192, 4, 226, 226, 226, 26, 224, 224, 224, 40, 255, 255, 255, 0, 255, 255, 255, 1, 229, 229, 229, 39, 225, 225, 225, 25, 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),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_o41n3\"]\nimage = SubResource(\"Image_xkapw\")\n\n[sub_resource type=\"Image\" id=\"Image_3hs4q\"]\ndata = {\n\"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, 4, 226, 226, 226, 26, 224, 224, 224, 41, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 41, 226, 226, 226, 26, 192, 192, 192, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 1, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 223, 223, 223, 47, 224, 224, 224, 8, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 226, 226, 226, 43, 227, 227, 227, 9, 255, 255, 255, 0, 227, 227, 227, 9, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 17, 255, 255, 255, 8, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 192, 192, 192, 4, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 224, 224, 224, 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, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 224, 224, 224, 40, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 255, 255, 255, 8, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 192, 192, 192, 4, 226, 226, 226, 43, 224, 224, 224, 8, 255, 255, 255, 0, 227, 227, 227, 9, 227, 227, 227, 18, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 101, 224, 224, 224, 49, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 255, 255, 255, 3, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 1, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 238, 224, 224, 224, 49, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 209, 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, 192, 192, 192, 4, 226, 226, 226, 26, 224, 224, 224, 40, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 188, 224, 224, 224, 112, 230, 230, 230, 10, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_6oiqe\"]\nimage = SubResource(\"Image_3hs4q\")\n\n[sub_resource type=\"Image\" id=\"Image_mvvke\"]\ndata = {\n\"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, 4, 226, 226, 226, 26, 224, 224, 224, 41, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 41, 226, 226, 226, 26, 192, 192, 192, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 1, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 223, 223, 223, 47, 224, 224, 224, 8, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 226, 226, 226, 43, 227, 227, 227, 9, 255, 255, 255, 0, 227, 227, 227, 9, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 17, 255, 255, 255, 8, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 192, 192, 192, 4, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 224, 224, 224, 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, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 224, 224, 224, 40, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 255, 255, 255, 8, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 192, 192, 192, 4, 226, 226, 226, 43, 224, 224, 224, 8, 255, 255, 255, 0, 226, 226, 226, 52, 225, 225, 225, 101, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 227, 227, 227, 9, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 255, 255, 255, 3, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 1, 255, 255, 255, 0, 227, 227, 227, 53, 224, 224, 224, 239, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 1, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 7, 224, 224, 224, 213, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 1, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 232, 232, 232, 11, 224, 224, 224, 113, 224, 224, 224, 188, 255, 255, 255, 0, 255, 255, 255, 1, 229, 229, 229, 39, 225, 225, 225, 25, 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),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_l0amb\"]\nimage = SubResource(\"Image_mvvke\")\n\n[sub_resource type=\"Image\" id=\"Image_tw7gd\"]\ndata = {\n\"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, 4, 226, 226, 226, 26, 224, 224, 224, 41, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 41, 226, 226, 226, 26, 192, 192, 192, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 1, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 223, 223, 223, 47, 224, 224, 224, 8, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 226, 226, 226, 43, 227, 227, 227, 9, 255, 255, 255, 0, 227, 227, 227, 9, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 17, 255, 255, 255, 8, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 192, 192, 192, 4, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 224, 224, 224, 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, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 1, 255, 255, 255, 1, 255, 255, 255, 1, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 202, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 85, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 224, 224, 224, 40, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 128, 224, 224, 224, 255, 224, 224, 224, 231, 224, 224, 224, 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, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 17, 224, 224, 224, 212, 229, 229, 229, 39, 255, 255, 255, 0, 227, 227, 227, 9, 227, 227, 227, 18, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 227, 227, 227, 9, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 255, 255, 255, 3, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 5, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 192, 192, 192, 4, 225, 225, 225, 25, 230, 230, 230, 40, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 40, 225, 225, 225, 25, 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),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_nonnc\"]\nimage = SubResource(\"Image_tw7gd\")\n\n[sub_resource type=\"Image\" id=\"Image_3q3qx\"]\ndata = {\n\"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, 4, 226, 226, 226, 26, 224, 224, 224, 41, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 41, 226, 226, 226, 26, 192, 192, 192, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 1, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 8, 255, 255, 255, 0, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 223, 223, 223, 47, 224, 224, 224, 8, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 229, 229, 229, 19, 224, 224, 224, 218, 227, 227, 227, 45, 255, 255, 255, 0, 227, 227, 227, 9, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 17, 255, 255, 255, 8, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 192, 192, 192, 4, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 131, 224, 224, 224, 255, 224, 224, 224, 234, 227, 227, 227, 45, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 202, 224, 224, 224, 252, 224, 224, 224, 252, 224, 224, 224, 82, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 224, 224, 224, 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, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 224, 224, 224, 40, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 255, 255, 255, 8, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 192, 192, 192, 4, 226, 226, 226, 43, 224, 224, 224, 8, 255, 255, 255, 0, 227, 227, 227, 9, 227, 227, 227, 18, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 227, 227, 227, 9, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 255, 255, 255, 3, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 1, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 192, 192, 192, 4, 226, 226, 226, 26, 224, 224, 224, 40, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 40, 225, 225, 225, 25, 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),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_d2btj\"]\nimage = SubResource(\"Image_3q3qx\")\n\n[sub_resource type=\"Image\" id=\"Image_3cynh\"]\ndata = {\n\"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, 230, 230, 230, 30, 225, 225, 225, 149, 224, 224, 224, 221, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 41, 226, 226, 226, 26, 192, 192, 192, 4, 255, 255, 255, 0, 255, 255, 255, 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, 224, 224, 224, 214, 224, 224, 224, 255, 224, 224, 224, 253, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 1, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 231, 231, 231, 31, 224, 224, 224, 224, 224, 224, 224, 252, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 223, 223, 223, 47, 224, 224, 224, 8, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 226, 226, 226, 43, 227, 227, 227, 9, 255, 255, 255, 0, 224, 224, 224, 32, 227, 227, 227, 63, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 17, 255, 255, 255, 8, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 192, 192, 192, 4, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 224, 224, 224, 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, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 224, 224, 224, 40, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 255, 255, 255, 8, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 192, 192, 192, 4, 226, 226, 226, 43, 224, 224, 224, 8, 255, 255, 255, 0, 227, 227, 227, 9, 227, 227, 227, 18, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 227, 227, 227, 9, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 255, 255, 255, 3, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 1, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 1, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 1, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 192, 192, 192, 4, 226, 226, 226, 26, 224, 224, 224, 40, 255, 255, 255, 0, 255, 255, 255, 1, 229, 229, 229, 39, 225, 225, 225, 25, 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),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_1bxo7\"]\nimage = SubResource(\"Image_3cynh\")\n\n[sub_resource type=\"AnimatedTexture\" id=\"AnimatedTexture_eylo1\"]\nframes = 8\nspeed_scale = 2.5\nframe_0/texture = SubResource(\"ImageTexture_nwpuj\")\nframe_0/duration = 0.2\nframe_1/texture = SubResource(\"ImageTexture_pdcj5\")\nframe_1/duration = 0.2\nframe_2/texture = SubResource(\"ImageTexture_o41n3\")\nframe_2/duration = 0.2\nframe_3/texture = SubResource(\"ImageTexture_6oiqe\")\nframe_3/duration = 0.2\nframe_4/texture = SubResource(\"ImageTexture_l0amb\")\nframe_4/duration = 0.2\nframe_5/texture = SubResource(\"ImageTexture_nonnc\")\nframe_5/duration = 0.2\nframe_6/texture = SubResource(\"ImageTexture_d2btj\")\nframe_6/duration = 0.2\nframe_7/texture = SubResource(\"ImageTexture_1bxo7\")\nframe_7/duration = 0.2\n\n[sub_resource type=\"Image\" id=\"Image_5n8ks\"]\ndata = {\n\"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, 224, 224, 224, 195, 224, 224, 224, 210, 224, 224, 224, 56, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 253, 224, 224, 224, 139, 224, 224, 224, 8, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 215, 224, 224, 224, 56, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 253, 224, 224, 224, 139, 224, 224, 224, 8, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 183, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 182, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 252, 225, 225, 225, 134, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 212, 226, 226, 226, 52, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 252, 225, 225, 225, 134, 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, 225, 225, 225, 191, 224, 224, 224, 206, 226, 226, 226, 52, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_dr7yj\"]\nimage = SubResource(\"Image_5n8ks\")\n\n[sub_resource type=\"Image\" id=\"Image_63duq\"]\ndata = {\n\"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, 224, 224, 224, 184, 224, 224, 224, 255, 224, 224, 224, 181, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 184, 224, 224, 224, 202, 228, 228, 228, 37, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 239, 224, 224, 224, 74, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 253, 224, 224, 224, 123, 255, 255, 255, 1, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 173, 234, 234, 234, 12, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 188, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 185, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 168, 230, 230, 230, 10, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 252, 225, 225, 225, 118, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 237, 226, 226, 226, 70, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 181, 224, 224, 224, 255, 224, 224, 224, 180, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 188, 224, 224, 224, 201, 225, 225, 225, 34, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_oh8cr\"]\nimage = SubResource(\"Image_63duq\")\n\n[sub_resource type=\"Image\" id=\"Image_lqrfl\"]\ndata = {\n\"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, 224, 224, 224, 196, 224, 224, 224, 196, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 24, 226, 226, 226, 60, 226, 226, 226, 60, 224, 224, 224, 255, 224, 224, 224, 255, 226, 226, 226, 60, 226, 226, 226, 60, 233, 233, 233, 23, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 5, 225, 225, 225, 134, 224, 224, 224, 254, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 254, 225, 225, 225, 133, 255, 255, 255, 5, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 67, 224, 224, 224, 231, 224, 224, 224, 230, 224, 224, 224, 66, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 231, 231, 231, 21, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 71, 224, 224, 224, 236, 224, 224, 224, 236, 224, 224, 224, 236, 224, 224, 224, 236, 224, 224, 224, 236, 224, 224, 224, 236, 224, 224, 224, 236, 224, 224, 224, 236, 224, 224, 224, 236, 224, 224, 224, 236, 224, 224, 224, 236, 224, 224, 224, 236, 224, 224, 224, 236, 224, 224, 224, 236, 225, 225, 225, 67, 226, 226, 226, 69, 224, 224, 224, 232, 224, 224, 224, 232, 224, 224, 224, 232, 224, 224, 224, 232, 224, 224, 224, 232, 224, 224, 224, 232, 224, 224, 224, 232, 224, 224, 224, 232, 224, 224, 224, 232, 224, 224, 224, 232, 224, 224, 224, 232, 224, 224, 224, 232, 224, 224, 224, 232, 224, 224, 224, 232, 224, 224, 224, 66, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 230, 230, 230, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 65, 224, 224, 224, 229, 224, 224, 224, 229, 224, 224, 224, 64, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 224, 224, 224, 132, 224, 224, 224, 253, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 253, 224, 224, 224, 130, 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, 224, 224, 224, 24, 224, 224, 224, 64, 224, 224, 224, 64, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 64, 224, 224, 224, 64, 233, 233, 233, 23, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 200, 224, 224, 224, 200, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_x1ivs\"]\nimage = SubResource(\"Image_lqrfl\")\n\n[sub_resource type=\"Image\" id=\"Image_fec2x\"]\ndata = {\n\"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, 237, 237, 237, 14, 224, 224, 224, 165, 224, 224, 224, 165, 237, 237, 237, 14, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 58, 225, 225, 225, 223, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 223, 225, 225, 225, 58, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 233, 233, 233, 23, 225, 225, 225, 124, 224, 224, 224, 128, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 128, 225, 225, 225, 124, 233, 233, 233, 23, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 233, 233, 233, 23, 225, 225, 225, 125, 224, 224, 224, 128, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 128, 225, 225, 225, 125, 233, 233, 233, 23, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 59, 224, 224, 224, 224, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 223, 225, 225, 225, 59, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 238, 238, 238, 15, 224, 224, 224, 165, 224, 224, 224, 165, 238, 238, 238, 15, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_i13wr\"]\nimage = SubResource(\"Image_fec2x\")\n\n[node name=\"MainPanel\" type=\"VSplitContainer\"]\nuse_parent_material = true\nclip_contents = true\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\noffset_right = -924.0\ngrow_horizontal = 2\ngrow_vertical = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\nsplit_offset = 200\nscript = ExtResource(\"1\")\n\n[node name=\"Panel\" type=\"PanelContainer\" parent=\".\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\n\n[node name=\"Tree\" type=\"Tree\" parent=\"Panel\"]\nuse_parent_material = true\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\ntheme_override_constants/icon_max_width = 16\ncolumns = 2\nallow_rmb_select = true\nhide_root = true\nselect_mode = 1\n\n[node name=\"discover_hint\" type=\"HBoxContainer\" parent=\"Panel\"]\nunique_name_in_owner = true\nvisible = false\nuse_parent_material = true\nlayout_mode = 2\nalignment = 1\n\n[node name=\"spinner\" type=\"Button\" parent=\"Panel/discover_hint\"]\nunique_name_in_owner = true\nclip_contents = true\ncustom_minimum_size = Vector2(64, 64)\nlayout_mode = 2\nsize_flags_stretch_ratio = 1.94\ndisabled = true\nbutton_mask = 0\ntext = \"Discover Tests\"\nicon = SubResource(\"AnimatedTexture_eylo1\")\nflat = true\nalignment = 2\n\n[node name=\"report\" type=\"PanelContainer\" parent=\".\"]\nclip_contents = true\nlayout_mode = 2\nsize_flags_horizontal = 11\nsize_flags_vertical = 11\n\n[node name=\"report_template\" type=\"RichTextLabel\" parent=\"report\"]\nuse_parent_material = true\nclip_contents = false\nlayout_mode = 2\nsize_flags_horizontal = 3\nauto_translate = false\nlocalize_numeral_system = false\nfocus_mode = 2\nbbcode_enabled = true\nfit_content = true\nselection_enabled = true\n\n[node name=\"ScrollContainer\" type=\"ScrollContainer\" parent=\"report\"]\nuse_parent_material = true\ncustom_minimum_size = Vector2(0, 80)\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 11\n\n[node name=\"list\" type=\"VBoxContainer\" parent=\"report/ScrollContainer\"]\nclip_contents = true\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\n\n[node name=\"contextMenu\" type=\"PopupMenu\" parent=\".\"]\nsize = Vector2i(133, 120)\nauto_translate = false\nitem_count = 5\nitem_0/text = \"Run\"\nitem_0/icon = SubResource(\"ImageTexture_dr7yj\")\nitem_0/id = 0\nitem_1/text = \"Debug\"\nitem_1/icon = SubResource(\"ImageTexture_oh8cr\")\nitem_1/id = 1\nitem_2/text = \"\"\nitem_2/id = 2\nitem_2/separator = true\nitem_3/text = \"Collapse All\"\nitem_3/icon = SubResource(\"ImageTexture_x1ivs\")\nitem_3/id = 3\nitem_4/text = \"Expand All\"\nitem_4/icon = SubResource(\"ImageTexture_i13wr\")\nitem_4/id = 4\n\n[connection signal=\"item_activated\" from=\"Panel/Tree\" to=\".\" method=\"_on_Tree_item_activated\"]\n[connection signal=\"item_mouse_selected\" from=\"Panel/Tree\" to=\".\" method=\"_on_tree_item_mouse_selected\"]\n[connection signal=\"item_selected\" from=\"Panel/Tree\" to=\".\" method=\"_on_Tree_item_selected\"]\n[connection signal=\"index_pressed\" from=\"contextMenu\" to=\".\" method=\"_on_context_m_index_pressed\"]\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/settings/GdUnitInputCapture.gd",
    "content": "@tool\nclass_name GdUnitInputCapture\nextends Control\n\nsignal input_completed(input_event: InputEventKey)\n\n\nvar _tween: Tween\nvar _input_event: InputEventKey\n\n\nfunc _ready() -> void:\n\treset()\n\tself_modulate = Color.WHITE\n\t_tween = create_tween()\n\t@warning_ignore(\"return_value_discarded\")\n\t_tween.set_loops()\n\t@warning_ignore(\"return_value_discarded\")\n\t_tween.tween_property(%Label, \"self_modulate\", Color(1, 1, 1, .8), 1.0).from_current().set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_IN_OUT)\n\n\nfunc reset() -> void:\n\t_input_event = InputEventKey.new()\n\n\nfunc _input(event: InputEvent) -> void:\n\tif not is_visible_in_tree():\n\t\treturn\n\tif event is InputEventKey and event.is_pressed() and not event.is_echo():\n\t\tvar _event := event as InputEventKey\n\t\tmatch _event.keycode:\n\t\t\tKEY_CTRL:\n\t\t\t\t_input_event.ctrl_pressed = true\n\t\t\tKEY_SHIFT:\n\t\t\t\t_input_event.shift_pressed = true\n\t\t\tKEY_ALT:\n\t\t\t\t_input_event.alt_pressed = true\n\t\t\tKEY_META:\n\t\t\t\t_input_event.meta_pressed = true\n\t\t\t_:\n\t\t\t\t_input_event.keycode = _event.keycode\n\t\t_apply_input_modifiers(_event)\n\t\taccept_event()\n\n\tif event is InputEventKey and not event.is_pressed():\n\t\tinput_completed.emit(_input_event)\n\t\thide()\n\n\nfunc _apply_input_modifiers(event: InputEvent) -> void:\n\tif event is InputEventWithModifiers:\n\t\tvar _event := event as InputEventWithModifiers\n\t\t_input_event.meta_pressed = _event.meta_pressed or _input_event.meta_pressed\n\t\t_input_event.alt_pressed = _event.alt_pressed or _input_event.alt_pressed\n\t\t_input_event.shift_pressed = _event.shift_pressed or _input_event.shift_pressed\n\t\t_input_event.ctrl_pressed = _event.ctrl_pressed or _input_event.ctrl_pressed\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/settings/GdUnitInputCapture.gd.uid",
    "content": "uid://d1c54d1d3fxng\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/settings/GdUnitInputCapture.tscn",
    "content": "[gd_scene load_steps=2 format=3 uid=\"uid://pmnkxrhglak5\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/gdUnit4/src/ui/settings/GdUnitInputCapture.gd\" id=\"1_gki1u\"]\n\n[node name=\"GdUnitInputMapper\" type=\"Control\"]\nmodulate = Color(0.929099, 0.929099, 0.929099, 0.936189)\ntop_level = true\nlayout_mode = 3\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\nscript = ExtResource(\"1_gki1u\")\n\n[node name=\"Label\" type=\"Label\" parent=\".\"]\nunique_name_in_owner = true\nself_modulate = Color(0.401913, 0.401913, 0.401913, 0.461723)\ntop_level = true\nlayout_mode = 1\nanchors_preset = 8\nanchor_left = 0.5\nanchor_top = 0.5\nanchor_right = 0.5\nanchor_bottom = 0.5\noffset_left = -60.5\noffset_top = -19.5\noffset_right = 60.5\noffset_bottom = 19.5\ngrow_horizontal = 2\ngrow_vertical = 2\ntheme_override_colors/font_color = Color(1, 1, 1, 1)\ntheme_override_font_sizes/font_size = 26\ntext = \"Press keys for shortcut\"\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd",
    "content": "@tool\nextends Window\n\nconst EAXAMPLE_URL := \"https://github.com/MikeSchulze/gdUnit4-examples/archive/refs/heads/master.zip\"\nconst GdUnitTools := preload (\"res://addons/gdUnit4/src/core/GdUnitTools.gd\")\nconst GdUnitUpdateClient = preload (\"res://addons/gdUnit4/src/update/GdUnitUpdateClient.gd\")\n\n@onready var _update_client: GdUnitUpdateClient = $GdUnitUpdateClient\n@onready var _version_label: RichTextLabel = %version\n@onready var _btn_install: Button = %btn_install_examples\n@onready var _progress_bar: ProgressBar = %ProgressBar\n@onready var _progress_text: Label = %progress_lbl\n@onready var _properties_template: Node = $property_template\n@onready var _properties_common: Node = % \"common-content\"\n@onready var _properties_ui: Node = % \"ui-content\"\n@onready var _properties_shortcuts: Node = % \"shortcut-content\"\n@onready var _properties_report: Node = % \"report-content\"\n@onready var _input_capture: GdUnitInputCapture = %GdUnitInputCapture\n@onready var _property_error: Window = % \"propertyError\"\n@onready var _tab_container: TabContainer = %Properties\n@onready var _update_tab: = %Update\n\nvar _font_size: float\n\n\nfunc _ready() -> void:\n\tset_name(\"GdUnitSettingsDialog\")\n\t# initialize for testing\n\tif not Engine.is_editor_hint():\n\t\tGdUnitSettings.setup()\n\tGdUnit4Version.init_version_label(_version_label)\n\t_font_size = GdUnitFonts.init_fonts(_version_label)\n\tsetup_properties(_properties_common, GdUnitSettings.COMMON_SETTINGS)\n\tsetup_properties(_properties_ui, GdUnitSettings.UI_SETTINGS)\n\tsetup_properties(_properties_report, GdUnitSettings.REPORT_SETTINGS)\n\tsetup_properties(_properties_shortcuts, GdUnitSettings.SHORTCUT_SETTINGS)\n\tcheck_for_update()\n\n\nfunc _sort_by_key(left: GdUnitProperty, right: GdUnitProperty) -> bool:\n\treturn left.name() < right.name()\n\n\nfunc setup_properties(properties_parent: Node, property_category: String) -> void:\n\t# Do remove first potential previous added properties (could be happened when the dlg is opened at twice)\n\tfor child in properties_parent.get_children():\n\t\tproperties_parent.remove_child(child)\n\n\tvar category_properties := GdUnitSettings.list_settings(property_category)\n\t# sort by key\n\tcategory_properties.sort_custom(_sort_by_key)\n\tvar theme_ := Theme.new()\n\ttheme_.set_constant(\"h_separation\", \"GridContainer\", 12)\n\tvar last_category := \"!\"\n\tvar min_size_overall := 0.0\n\tvar labels := []\n\tvar inputs := []\n\tvar info_labels := []\n\tvar grid: GridContainer = null\n\tfor p in category_properties:\n\t\tvar min_size_ := 0.0\n\t\tvar property: GdUnitProperty = p\n\t\tvar current_category := property.category()\n\t\tif not grid or current_category != last_category:\n\t\t\tgrid = GridContainer.new()\n\t\t\tgrid.columns = 4\n\t\t\tgrid.theme = theme_\n\n\t\t\tvar sub_category: Node = _properties_template.get_child(3).duplicate()\n\t\t\tsub_category.get_child(0).text = current_category.capitalize()\n\t\t\tsub_category.custom_minimum_size.y = _font_size + 16\n\t\t\tproperties_parent.add_child(sub_category)\n\t\t\tproperties_parent.add_child(grid)\n\t\t\tlast_category = current_category\n\t\t# property name\n\t\tvar label: Label = _properties_template.get_child(0).duplicate()\n\t\tlabel.text = _to_human_readable(property.name())\n\t\tlabels.append(label)\n\t\tgrid.add_child(label)\n\n\t\t# property reset btn\n\t\tvar reset_btn: Button = _properties_template.get_child(1).duplicate()\n\t\treset_btn.icon = _get_btn_icon(\"Reload\")\n\t\treset_btn.disabled = property.value() == property.default()\n\t\tgrid.add_child(reset_btn)\n\n\t\t# property type specific input element\n\t\tvar input: Node = _create_input_element(property, reset_btn)\n\t\tinputs.append(input)\n\t\tgrid.add_child(input)\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\treset_btn.pressed.connect(_on_btn_property_reset_pressed.bind(property, input, reset_btn))\n\t\t# property help text\n\t\tvar info: Node = _properties_template.get_child(2).duplicate()\n\t\tinfo.text = property.help()\n\t\tinfo_labels.append(info)\n\t\tgrid.add_child(info)\n\t\tif min_size_overall < min_size_:\n\t\t\tmin_size_overall = min_size_\n\n\tfor controls: Array in [labels, inputs, info_labels]:\n\t\tvar _size: float = controls.map(func(c: Control) -> float: return c.size.x).max()\n\t\tmin_size_overall += _size\n\t\tfor control: Control in controls:\n\t\t\tcontrol.custom_minimum_size.x = _size\n\tproperties_parent.custom_minimum_size.x = min_size_overall\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc _create_input_element(property: GdUnitProperty, reset_btn: Button) -> Node:\n\tif property.is_selectable_value():\n\t\tvar options := OptionButton.new()\n\t\toptions.alignment = HORIZONTAL_ALIGNMENT_CENTER\n\t\tfor value in property.value_set():\n\t\t\toptions.add_item(value)\n\t\toptions.item_selected.connect(_on_option_selected.bind(property, reset_btn))\n\t\toptions.select(property.value())\n\t\treturn options\n\tif property.type() == TYPE_BOOL:\n\t\tvar check_btn := CheckButton.new()\n\t\tcheck_btn.toggled.connect(_on_property_text_changed.bind(property, reset_btn))\n\t\tcheck_btn.button_pressed = property.value()\n\t\treturn check_btn\n\tif property.type() in [TYPE_INT, TYPE_STRING]:\n\t\tvar input := LineEdit.new()\n\t\tinput.text_changed.connect(_on_property_text_changed.bind(property, reset_btn))\n\t\tinput.set_context_menu_enabled(false)\n\t\tinput.set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER)\n\t\tinput.set_expand_to_text_length_enabled(true)\n\t\tinput.text = str(property.value())\n\t\treturn input\n\tif property.type() == TYPE_PACKED_INT32_ARRAY:\n\t\tvar key_input_button := Button.new()\n\t\tkey_input_button.text = to_shortcut(property.value())\n\t\tkey_input_button.pressed.connect(_on_shortcut_change.bind(key_input_button, property, reset_btn))\n\t\treturn key_input_button\n\treturn Control.new()\n\n\nfunc to_shortcut(keys: PackedInt32Array) -> String:\n\tvar input_event := InputEventKey.new()\n\tfor key in keys:\n\t\tmatch key:\n\t\t\tKEY_CTRL: input_event.ctrl_pressed = true\n\t\t\tKEY_SHIFT: input_event.shift_pressed = true\n\t\t\tKEY_ALT: input_event.alt_pressed = true\n\t\t\tKEY_META: input_event.meta_pressed = true\n\t\t\t_:\n\t\t\t\tinput_event.keycode = key as Key\n\treturn input_event.as_text()\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc to_keys(input_event: InputEventKey) -> PackedInt32Array:\n\tvar keys := PackedInt32Array()\n\tif input_event.ctrl_pressed:\n\t\tkeys.append(KEY_CTRL)\n\tif input_event.shift_pressed:\n\t\tkeys.append(KEY_SHIFT)\n\tif input_event.alt_pressed:\n\t\tkeys.append(KEY_ALT)\n\tif input_event.meta_pressed:\n\t\tkeys.append(KEY_META)\n\tkeys.append(input_event.keycode)\n\treturn keys\n\n\nfunc _to_human_readable(value: String) -> String:\n\treturn value.split(\"/\")[-1].capitalize()\n\n\nfunc _get_btn_icon(p_name: String) -> Texture2D:\n\tif not Engine.is_editor_hint():\n\t\tvar placeholder := PlaceholderTexture2D.new()\n\t\tplaceholder.size = Vector2(8, 8)\n\t\treturn placeholder\n\treturn GdUnitUiTools.get_icon(p_name)\n\n\nfunc _install_examples() -> void:\n\t_init_progress(5)\n\tupdate_progress(\"Downloading examples\")\n\tawait get_tree().process_frame\n\tvar tmp_path := GdUnitFileAccess.create_temp_dir(\"download\")\n\tvar zip_file := tmp_path + \"/examples.zip\"\n\tvar response: GdUnitUpdateClient.HttpResponse = await _update_client.request_zip_package(EAXAMPLE_URL, zip_file)\n\tif response.code() != 200:\n\t\tpush_warning(\"Examples cannot be retrieved from GitHub! \\n Error code: %d : %s\" % [response.code(), response.response()])\n\t\tupdate_progress(\"Install examples failed! Try it later again.\")\n\t\tawait get_tree().create_timer(3).timeout\n\t\tstop_progress()\n\t\treturn\n\t# extract zip to tmp\n\tupdate_progress(\"Install examples into project\")\n\tvar result := GdUnitFileAccess.extract_zip(zip_file, \"res://gdUnit4-examples/\")\n\tif result.is_error():\n\t\tupdate_progress(\"Install examples failed! %s\" % result.error_message())\n\t\tawait get_tree().create_timer(3).timeout\n\t\tstop_progress()\n\t\treturn\n\tupdate_progress(\"Refresh project\")\n\tawait rescan(true)\n\tupdate_progress(\"Examples successfully installed\")\n\tawait get_tree().create_timer(3).timeout\n\tstop_progress()\n\n\nfunc rescan(update_scripts:=false) -> void:\n\tawait get_tree().idle_frame\n\tvar fs := EditorInterface.get_resource_filesystem()\n\tfs.scan_sources()\n\twhile fs.is_scanning():\n\t\tawait get_tree().create_timer(1).timeout\n\tif update_scripts:\n\t\tEditorInterface.get_resource_filesystem().update_script_classes()\n\n\nfunc check_for_update() -> void:\n\tif not GdUnitSettings.is_update_notification_enabled():\n\t\treturn\n\tvar response :GdUnitUpdateClient.HttpResponse = await _update_client.request_latest_version()\n\tif response.status() != 200:\n\t\tprinterr(\"Latest version information cannot be retrieved from GitHub!\")\n\t\tprinterr(\"Error:  %s\" % response.response())\n\t\treturn\n\tvar latest_version := _update_client.extract_latest_version(response)\n\tif latest_version.is_greater(GdUnit4Version.current()):\n\t\tvar tab_index := _tab_container.get_tab_idx_from_control(_update_tab)\n\t\t_tab_container.set_tab_button_icon(tab_index, GdUnitUiTools.get_icon(\"Notification\", Color.YELLOW))\n\t\t_tab_container.set_tab_tooltip(tab_index, \"An new update is available.\")\n\n\nfunc _on_btn_report_bug_pressed() -> void:\n\t@warning_ignore(\"return_value_discarded\")\n\tOS.shell_open(\"https://github.com/MikeSchulze/gdUnit4/issues/new?assignees=MikeSchulze&labels=bug&projects=projects%2F5&template=bug_report.yml&title=GD-XXX%3A+Describe+the+issue+briefly\")\n\n\nfunc _on_btn_request_feature_pressed() -> void:\n\t@warning_ignore(\"return_value_discarded\")\n\tOS.shell_open(\"https://github.com/MikeSchulze/gdUnit4/issues/new?assignees=MikeSchulze&labels=enhancement&projects=&template=feature_request.md&title=\")\n\n\nfunc _on_btn_install_examples_pressed() -> void:\n\t_btn_install.disabled = true\n\tawait _install_examples()\n\t_btn_install.disabled = false\n\n\nfunc _on_btn_close_pressed() -> void:\n\thide()\n\n\nfunc _on_btn_property_reset_pressed(property: GdUnitProperty, input: Node, reset_btn: Button) -> void:\n\tif input is CheckButton:\n\t\tinput.button_pressed = property.default()\n\telif input is LineEdit:\n\t\tinput.text = str(property.default())\n\t\t# we have to update manually for text input fields because of no change event is emited\n\t\t_on_property_text_changed(property.default(), property, reset_btn)\n\telif input is OptionButton:\n\t\tinput.select(0)\n\t\t_on_option_selected(0, property, reset_btn)\n\telif input is Button:\n\t\tinput.text = to_shortcut(property.default())\n\t\t_on_property_text_changed(property.default(), property, reset_btn)\n\n\nfunc _on_property_text_changed(new_value: Variant, property: GdUnitProperty, reset_btn: Button) -> void:\n\tproperty.set_value(new_value)\n\treset_btn.disabled = property.value() == property.default()\n\tvar error: Variant = GdUnitSettings.update_property(property)\n\tif error:\n\t\tvar label: Label = _property_error.get_child(0) as Label\n\t\tlabel.set_text(error)\n\t\tvar control := gui_get_focus_owner()\n\t\t_property_error.show()\n\t\tif control != null:\n\t\t\t_property_error.position = control.global_position + Vector2(self.position) + Vector2(40, 40)\n\n\nfunc _on_option_selected(index: int, property: GdUnitProperty, reset_btn: Button) -> void:\n\tproperty.set_value(index)\n\treset_btn.disabled = property.value() == property.default()\n\tGdUnitSettings.update_property(property)\n\n\nfunc _on_shortcut_change(input_button: Button, property: GdUnitProperty, reset_btn: Button) -> void:\n\t_input_capture.set_custom_minimum_size(_properties_shortcuts.get_size())\n\t_input_capture.visible = true\n\t_input_capture.show()\n\t_properties_shortcuts.visible = false\n\tset_process_input(false)\n\t_input_capture.reset()\n\tvar input_event: InputEventKey = await _input_capture.input_completed\n\tinput_button.text = input_event.as_text()\n\t_on_property_text_changed(to_keys(input_event), property, reset_btn)\n\t_properties_shortcuts.visible = true\n\tset_process_input(true)\n\n\nfunc _init_progress(max_value: int) -> void:\n\t_progress_bar.visible = true\n\t_progress_bar.max_value = max_value\n\t_progress_bar.value = 0\n\n\nfunc _progress() -> void:\n\t_progress_bar.value += 1\n\n\nfunc stop_progress() -> void:\n\t_progress_bar.visible = false\n\n\nfunc update_progress(message: String) -> void:\n\t_progress_text.text = message\n\t_progress_bar.value += 1\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd.uid",
    "content": "uid://bwo1810dlvcxe\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.tscn",
    "content": "[gd_scene load_steps=8 format=3 uid=\"uid://dwgat6j2u77g4\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd\" id=\"2\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bkh022wwqq7s3\" path=\"res://addons/gdUnit4/src/ui/settings/logo.png\" id=\"3_isfyl\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://dte0m2endcgtu\" path=\"res://addons/gdUnit4/src/ui/templates/TestSuiteTemplate.tscn\" id=\"4\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://0xyeci1tqebj\" path=\"res://addons/gdUnit4/src/update/GdUnitUpdateNotify.tscn\" id=\"5_n1jtv\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://pmnkxrhglak5\" path=\"res://addons/gdUnit4/src/ui/settings/GdUnitInputCapture.tscn\" id=\"5_xu3j8\"]\n[ext_resource type=\"Script\" path=\"res://addons/gdUnit4/src/update/GdUnitUpdateClient.gd\" id=\"8_2ggr0\"]\n\n[sub_resource type=\"StyleBoxFlat\" id=\"StyleBoxFlat_hbbq5\"]\ncontent_margin_left = 10.0\ncontent_margin_right = 10.0\nbg_color = Color(0.172549, 0.113725, 0.141176, 1)\nborder_width_left = 4\nborder_width_top = 4\nborder_width_right = 4\nborder_width_bottom = 4\nborder_color = Color(0.87451, 0.0705882, 0.160784, 1)\nborder_blend = true\ncorner_radius_top_left = 8\ncorner_radius_top_right = 8\ncorner_radius_bottom_right = 8\ncorner_radius_bottom_left = 8\nshadow_color = Color(0, 0, 0, 0.756863)\nshadow_size = 10\nshadow_offset = Vector2(10, 10)\n\n[node name=\"GdUnitSettingsDialog\" type=\"Window\"]\ndisable_3d = true\ngui_embed_subwindows = true\ntitle = \"GdUnit4 Settings\"\ninitial_position = 1\nsize = Vector2i(1400, 600)\nvisible = false\nwrap_controls = true\nexclusive = true\nextend_to_title = true\nscript = ExtResource(\"2\")\n\n[node name=\"property_template\" type=\"Control\" parent=\".\"]\nvisible = false\nlayout_mode = 3\nanchors_preset = 0\noffset_left = 4.0\noffset_top = 4.0\noffset_right = 4.0\noffset_bottom = 4.0\nsize_flags_horizontal = 0\n\n[node name=\"Label\" type=\"Label\" parent=\"property_template\"]\nlayout_mode = 1\nanchors_preset = 4\nanchor_top = 0.5\nanchor_bottom = 0.5\noffset_top = -11.5\noffset_right = 1.0\noffset_bottom = 11.5\ngrow_vertical = 2\n\n[node name=\"btn_reset\" type=\"Button\" parent=\"property_template\"]\nlayout_mode = 0\noffset_right = 12.0\noffset_bottom = 40.0\ntooltip_text = \"Reset to default value\"\nclip_text = true\n\n[node name=\"info\" type=\"Label\" parent=\"property_template\"]\nclip_contents = true\nlayout_mode = 1\nanchors_preset = 4\nanchor_top = 0.5\nanchor_bottom = 0.5\noffset_top = -11.5\noffset_right = 316.0\noffset_bottom = 11.5\ngrow_vertical = 2\nsize_flags_horizontal = 3\nmax_lines_visible = 1\n\n[node name=\"sub_category\" type=\"Panel\" parent=\"property_template\"]\nlayout_mode = 1\nanchors_preset = 10\nanchor_right = 1.0\noffset_right = -220.0\ngrow_horizontal = 2\nsize_flags_horizontal = 3\n\n[node name=\"Label\" type=\"Label\" parent=\"property_template/sub_category\"]\nlayout_mode = 1\nanchors_preset = 4\nanchor_top = 0.5\nanchor_bottom = 0.5\noffset_left = 4.0\noffset_top = -11.5\noffset_right = 5.0\noffset_bottom = 11.5\ngrow_vertical = 2\ntheme_override_colors/font_color = Color(0.439216, 0.45098, 1, 1)\n\n[node name=\"GdUnitUpdateClient\" type=\"Node\" parent=\".\"]\nscript = ExtResource(\"8_2ggr0\")\n\n[node name=\"Panel\" type=\"Panel\" parent=\".\"]\nuse_parent_material = true\nclip_contents = true\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\n\n[node name=\"PanelContainer\" type=\"PanelContainer\" parent=\"Panel\"]\nlayout_mode = 1\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\n\n[node name=\"v\" type=\"VBoxContainer\" parent=\"Panel/PanelContainer\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\n\n[node name=\"MarginContainer\" type=\"MarginContainer\" parent=\"Panel/PanelContainer/v\"]\nuse_parent_material = true\nlayout_mode = 2\nsize_flags_vertical = 3\ntheme_override_constants/margin_left = 4\ntheme_override_constants/margin_top = 4\ntheme_override_constants/margin_right = 4\n\n[node name=\"GridContainer\" type=\"HBoxContainer\" parent=\"Panel/PanelContainer/v/MarginContainer\"]\nuse_parent_material = true\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\n\n[node name=\"PanelContainer\" type=\"MarginContainer\" parent=\"Panel/PanelContainer/v/MarginContainer/GridContainer\"]\nuse_parent_material = true\nlayout_mode = 2\nsize_flags_vertical = 3\n\n[node name=\"Panel\" type=\"VBoxContainer\" parent=\"Panel/PanelContainer/v/MarginContainer/GridContainer/PanelContainer\"]\nuse_parent_material = true\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\n\n[node name=\"CenterContainer\" type=\"CenterContainer\" parent=\"Panel/PanelContainer/v/MarginContainer/GridContainer/PanelContainer/Panel\"]\nuse_parent_material = true\nlayout_mode = 2\nsize_flags_horizontal = 3\n\n[node name=\"logo\" type=\"TextureRect\" parent=\"Panel/PanelContainer/v/MarginContainer/GridContainer/PanelContainer/Panel/CenterContainer\"]\ncustom_minimum_size = Vector2(120, 120)\nlayout_mode = 2\nsize_flags_horizontal = 0\nsize_flags_vertical = 4\ntexture = ExtResource(\"3_isfyl\")\nexpand_mode = 1\nstretch_mode = 5\n\n[node name=\"CenterContainer2\" type=\"MarginContainer\" parent=\"Panel/PanelContainer/v/MarginContainer/GridContainer/PanelContainer/Panel\"]\nuse_parent_material = true\ncustom_minimum_size = Vector2(0, 30)\nlayout_mode = 2\nsize_flags_horizontal = 3\n\n[node name=\"version\" type=\"RichTextLabel\" parent=\"Panel/PanelContainer/v/MarginContainer/GridContainer/PanelContainer/Panel/CenterContainer2\"]\nunique_name_in_owner = true\nauto_translate_mode = 2\nuse_parent_material = true\nclip_contents = false\nlayout_mode = 2\nsize_flags_horizontal = 3\nlocalize_numeral_system = false\nbbcode_enabled = true\nscroll_active = false\nmeta_underlined = false\n\n[node name=\"VBoxContainer\" type=\"VBoxContainer\" parent=\"Panel/PanelContainer/v/MarginContainer/GridContainer/PanelContainer\"]\ncustom_minimum_size = Vector2(200, 0)\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\nalignment = 2\n\n[node name=\"btn_report_bug\" type=\"Button\" parent=\"Panel/PanelContainer/v/MarginContainer/GridContainer/PanelContainer/VBoxContainer\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\ntooltip_text = \"Press to create a bug report\"\ntext = \"Report Bug\"\n\n[node name=\"btn_request_feature\" type=\"Button\" parent=\"Panel/PanelContainer/v/MarginContainer/GridContainer/PanelContainer/VBoxContainer\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\ntooltip_text = \"Press to create a feature request\"\ntext = \"Request Feature\"\n\n[node name=\"btn_install_examples\" type=\"Button\" parent=\"Panel/PanelContainer/v/MarginContainer/GridContainer/PanelContainer/VBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 3\ntooltip_text = \"Press to install the advanced test examples\"\ndisabled = true\ntext = \"Install Examples\"\n\n[node name=\"Properties\" type=\"TabContainer\" parent=\"Panel/PanelContainer/v/MarginContainer/GridContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 3\ncurrent_tab = 0\n\n[node name=\"Common\" type=\"ScrollContainer\" parent=\"Panel/PanelContainer/v/MarginContainer/GridContainer/Properties\"]\nlayout_mode = 2\nmetadata/_tab_index = 0\n\n[node name=\"common-content\" type=\"VBoxContainer\" parent=\"Panel/PanelContainer/v/MarginContainer/GridContainer/Properties/Common\"]\nunique_name_in_owner = true\nclip_contents = true\ncustom_minimum_size = Vector2(1026, 0)\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\n\n[node name=\"UI\" type=\"ScrollContainer\" parent=\"Panel/PanelContainer/v/MarginContainer/GridContainer/Properties\"]\nvisible = false\nlayout_mode = 2\nmetadata/_tab_index = 1\n\n[node name=\"ui-content\" type=\"VBoxContainer\" parent=\"Panel/PanelContainer/v/MarginContainer/GridContainer/Properties/UI\"]\nunique_name_in_owner = true\nclip_contents = true\ncustom_minimum_size = Vector2(741, 0)\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\n\n[node name=\"Shortcuts\" type=\"ScrollContainer\" parent=\"Panel/PanelContainer/v/MarginContainer/GridContainer/Properties\"]\nvisible = false\nlayout_mode = 2\nmetadata/_tab_index = 2\n\n[node name=\"shortcut-content\" type=\"VBoxContainer\" parent=\"Panel/PanelContainer/v/MarginContainer/GridContainer/Properties/Shortcuts\"]\nunique_name_in_owner = true\nclip_contents = true\ncustom_minimum_size = Vector2(683, 0)\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\n\n[node name=\"Report\" type=\"ScrollContainer\" parent=\"Panel/PanelContainer/v/MarginContainer/GridContainer/Properties\"]\nvisible = false\nlayout_mode = 2\nmetadata/_tab_index = 3\n\n[node name=\"report-content\" type=\"VBoxContainer\" parent=\"Panel/PanelContainer/v/MarginContainer/GridContainer/Properties/Report\"]\nunique_name_in_owner = true\nclip_contents = true\ncustom_minimum_size = Vector2(667, 0)\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\n\n[node name=\"Templates\" parent=\"Panel/PanelContainer/v/MarginContainer/GridContainer/Properties\" instance=ExtResource(\"4\")]\nvisible = false\nlayout_mode = 2\nmetadata/_tab_index = 4\n\n[node name=\"Update\" parent=\"Panel/PanelContainer/v/MarginContainer/GridContainer/Properties\" instance=ExtResource(\"5_n1jtv\")]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\nmetadata/_tab_index = 5\n\n[node name=\"GdUnitInputCapture\" parent=\"Panel/PanelContainer/v/MarginContainer/GridContainer/Properties\" instance=ExtResource(\"5_xu3j8\")]\nunique_name_in_owner = true\nvisible = false\nmodulate = Color(1.54884e-09, 1.54884e-09, 1.54884e-09, 0.1)\nz_index = 1\nz_as_relative = false\nlayout_mode = 2\nsize_flags_horizontal = 1\nsize_flags_vertical = 1\n\n[node name=\"propertyError\" type=\"PopupPanel\" parent=\"Panel/PanelContainer/v/MarginContainer/GridContainer/Properties\"]\nunique_name_in_owner = true\ninitial_position = 1\nsize = Vector2i(400, 100)\ntheme_override_styles/panel = SubResource(\"StyleBoxFlat_hbbq5\")\n\n[node name=\"Label\" type=\"Label\" parent=\"Panel/PanelContainer/v/MarginContainer/GridContainer/Properties/propertyError\"]\noffset_left = 10.0\noffset_top = 4.0\noffset_right = 390.0\noffset_bottom = 96.0\ntheme_override_colors/font_color = Color(0.858824, 0, 0.109804, 1)\nhorizontal_alignment = 1\nvertical_alignment = 1\n\n[node name=\"MarginContainer2\" type=\"MarginContainer\" parent=\"Panel/PanelContainer/v\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\ntheme_override_constants/margin_left = 4\ntheme_override_constants/margin_right = 4\ntheme_override_constants/margin_bottom = 4\n\n[node name=\"HBoxContainer\" type=\"HBoxContainer\" parent=\"Panel/PanelContainer/v/MarginContainer2\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\nalignment = 2\n\n[node name=\"ProgressBar\" type=\"ProgressBar\" parent=\"Panel/PanelContainer/v/MarginContainer2/HBoxContainer\"]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\n\n[node name=\"progress_lbl\" type=\"Label\" parent=\"Panel/PanelContainer/v/MarginContainer2/HBoxContainer/ProgressBar\"]\nunique_name_in_owner = true\nlayout_mode = 1\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\nclip_text = true\n\n[node name=\"btn_close\" type=\"Button\" parent=\"Panel/PanelContainer/v/MarginContainer2/HBoxContainer\"]\ncustom_minimum_size = Vector2(200, 0)\nlayout_mode = 2\ntext = \"Close\"\n\n[connection signal=\"close_requested\" from=\".\" to=\".\" method=\"_on_btn_close_pressed\"]\n[connection signal=\"pressed\" from=\"Panel/PanelContainer/v/MarginContainer/GridContainer/PanelContainer/VBoxContainer/btn_report_bug\" to=\".\" method=\"_on_btn_report_bug_pressed\"]\n[connection signal=\"pressed\" from=\"Panel/PanelContainer/v/MarginContainer/GridContainer/PanelContainer/VBoxContainer/btn_request_feature\" to=\".\" method=\"_on_btn_request_feature_pressed\"]\n[connection signal=\"pressed\" from=\"Panel/PanelContainer/v/MarginContainer/GridContainer/PanelContainer/VBoxContainer/btn_install_examples\" to=\".\" method=\"_on_btn_install_examples_pressed\"]\n[connection signal=\"pressed\" from=\"Panel/PanelContainer/v/MarginContainer2/HBoxContainer/btn_close\" to=\".\" method=\"_on_btn_close_pressed\"]\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/settings/logo.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bkh022wwqq7s3\"\npath=\"res://.godot/imported/logo.png-deda0e4ba02a0b9e4e4a830029a5817f.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/gdUnit4/src/ui/settings/logo.png\"\ndest_files=[\"res://.godot/imported/logo.png-deda0e4ba02a0b9e4e4a830029a5817f.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/templates/TestSuiteTemplate.gd",
    "content": "@tool\nextends MarginContainer\n\n@onready var _template_editor :CodeEdit = $VBoxContainer/EdiorLayout/Editor\n@onready var _tags_editor :CodeEdit = $Tags/MarginContainer/TextEdit\n@onready var _title_bar :Panel = $VBoxContainer/sub_category\n@onready var _save_button :Button = $VBoxContainer/Panel/HBoxContainer/Save\n@onready var _selected_type :OptionButton = $VBoxContainer/EdiorLayout/Editor/MarginContainer/HBoxContainer/SelectType\n@onready var _show_tags  :PopupPanel = $Tags\n\n\nvar gd_key_words :PackedStringArray = [\"extends\", \"class_name\", \"const\", \"var\", \"onready\", \"func\", \"void\", \"pass\"]\nvar gdunit_key_words :PackedStringArray = [\"GdUnitTestSuite\", \"before\", \"after\", \"before_test\", \"after_test\"]\nvar _selected_template :int\n\n\nfunc _ready() -> void:\n\tsetup_editor_colors()\n\tsetup_fonts()\n\tsetup_supported_types()\n\tload_template(GdUnitTestSuiteTemplate.TEMPLATE_ID_GD)\n\tsetup_tags_help()\n\n\nfunc _notification(what :int) -> void:\n\tif what == EditorSettings.NOTIFICATION_EDITOR_SETTINGS_CHANGED:\n\t\tsetup_fonts()\n\n\nfunc setup_editor_colors() -> void:\n\tif not Engine.is_editor_hint():\n\t\treturn\n\n\tvar background_color := get_editor_color(\"text_editor/theme/highlighting/background_color\", Color(0.1155, 0.132, 0.1595, 1))\n\tvar text_color := get_editor_color(\"text_editor/theme/highlighting/text_color\", Color(0.8025, 0.81, 0.8225, 1))\n\tvar selection_color := get_editor_color(\"text_editor/theme/highlighting/selection_color\", Color(0.44, 0.73, 0.98, 0.4))\n\n\tfor e :CodeEdit in [_template_editor, _tags_editor]:\n\t\tvar editor :CodeEdit = e\n\t\teditor.add_theme_color_override(\"background_color\", background_color)\n\t\teditor.add_theme_color_override(\"font_color\", text_color)\n\t\teditor.add_theme_color_override(\"font_readonly_color\", text_color)\n\t\teditor.add_theme_color_override(\"font_selected_color\", selection_color)\n\t\tsetup_highlighter(editor)\n\n\nfunc setup_highlighter(editor :CodeEdit) -> void:\n\tvar highlighter := CodeHighlighter.new()\n\teditor.set_syntax_highlighter(highlighter)\n\tvar number_color := get_editor_color(\"text_editor/theme/highlighting/number_color\", Color(0.63, 1, 0.88, 1))\n\tvar symbol_color := get_editor_color(\"text_editor/theme/highlighting/symbol_color\", Color(0.67, 0.79, 1, 1))\n\tvar function_color := get_editor_color(\"text_editor/theme/highlighting/function_color\", Color(0.34, 0.7, 1, 1))\n\tvar member_variable_color := get_editor_color(\"text_editor/theme/highlighting/member_variable_color\", Color(0.736, 0.88, 1, 1))\n\tvar comment_color := get_editor_color(\"text_editor/theme/highlighting/comment_color\", Color(0.8025, 0.81, 0.8225, 0.5))\n\tvar keyword_color := get_editor_color(\"text_editor/theme/highlighting/keyword_color\", Color(1, 0.44, 0.52, 1))\n\tvar base_type_color := get_editor_color(\"text_editor/theme/highlighting/base_type_color\", Color(0.26, 1, 0.76, 1))\n\tvar annotation_color := get_editor_color(\"text_editor/theme/highlighting/gdscript/annotation_color\", Color(1, 0.7, 0.45, 1))\n\n\thighlighter.clear_color_regions()\n\thighlighter.clear_keyword_colors()\n\thighlighter.add_color_region(\"#\", \"\", comment_color, true)\n\thighlighter.add_color_region(\"${\", \"}\", Color.YELLOW)\n\thighlighter.add_color_region(\"'\", \"'\", Color.YELLOW)\n\thighlighter.add_color_region(\"\\\"\", \"\\\"\", Color.YELLOW)\n\thighlighter.number_color = number_color\n\thighlighter.symbol_color = symbol_color\n\thighlighter.function_color = function_color\n\thighlighter.member_variable_color = member_variable_color\n\thighlighter.add_keyword_color(\"@\", annotation_color)\n\thighlighter.add_keyword_color(\"warning_ignore\", annotation_color)\n\tfor word in gd_key_words:\n\t\thighlighter.add_keyword_color(word, keyword_color)\n\tfor word in gdunit_key_words:\n\t\thighlighter.add_keyword_color(word, base_type_color)\n\n\n## Using this function to avoid null references to colors on inital Godot installations.\n## For more details show https://github.com/MikeSchulze/gdUnit4/issues/533\nfunc get_editor_color(property_name: String, default: Color) -> Color:\n\tvar settings := EditorInterface.get_editor_settings()\n\treturn settings.get_setting(property_name) if settings.has_setting(property_name) else default\n\n\nfunc setup_fonts() -> void:\n\tif _template_editor:\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tGdUnitFonts.init_fonts(_template_editor)\n\t\tvar font_size := GdUnitFonts.init_fonts(_tags_editor)\n\t\t_title_bar.size.y = font_size + 16\n\t\t_title_bar.custom_minimum_size.y = font_size + 16\n\n\nfunc setup_supported_types() -> void:\n\t_selected_type.clear()\n\t_selected_type.add_item(\"GD - GDScript\", GdUnitTestSuiteTemplate.TEMPLATE_ID_GD)\n\t_selected_type.add_item(\"C# - CSharpScript\", GdUnitTestSuiteTemplate.TEMPLATE_ID_CS)\n\n\nfunc setup_tags_help() -> void:\n\t_tags_editor.set_text(GdUnitTestSuiteTemplate.load_tags(_selected_template))\n\n\nfunc load_template(template_id :int) -> void:\n\t_selected_template = template_id\n\t_template_editor.set_text(GdUnitTestSuiteTemplate.load_template(template_id))\n\n\nfunc _on_Restore_pressed() -> void:\n\t_template_editor.set_text(GdUnitTestSuiteTemplate.default_template(_selected_template))\n\tGdUnitTestSuiteTemplate.reset_to_default(_selected_template)\n\t_save_button.disabled = true\n\n\nfunc _on_Save_pressed() -> void:\n\tGdUnitTestSuiteTemplate.save_template(_selected_template, _template_editor.get_text())\n\t_save_button.disabled = true\n\n\nfunc _on_Tags_pressed() -> void:\n\t_show_tags.popup_centered_ratio(.5)\n\n\nfunc _on_Editor_text_changed() -> void:\n\t_save_button.disabled = false\n\n\nfunc _on_SelectType_item_selected(index :int) -> void:\n\tload_template(_selected_type.get_item_id(index))\n\tsetup_tags_help()\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/templates/TestSuiteTemplate.gd.uid",
    "content": "uid://dar5oonhmywc0\n"
  },
  {
    "path": "addons/gdUnit4/src/ui/templates/TestSuiteTemplate.tscn",
    "content": "[gd_scene load_steps=2 format=3 uid=\"uid://dte0m2endcgtu\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/gdUnit4/src/ui/templates/TestSuiteTemplate.gd\" id=\"1\"]\n\n[node name=\"TestSuiteTemplate\" type=\"MarginContainer\"]\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\nscript = ExtResource(\"1\")\n\n[node name=\"VBoxContainer\" type=\"VBoxContainer\" parent=\".\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\n\n[node name=\"sub_category\" type=\"Panel\" parent=\"VBoxContainer\"]\ncustom_minimum_size = Vector2(0, 30)\nlayout_mode = 2\nsize_flags_horizontal = 3\n\n[node name=\"Label\" type=\"Label\" parent=\"VBoxContainer/sub_category\"]\nlayout_mode = 1\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\noffset_left = 4.0\noffset_right = 4.0\noffset_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\ntext = \"Test Suite Template\n\"\n\n[node name=\"EdiorLayout\" type=\"VBoxContainer\" parent=\"VBoxContainer\"]\nlayout_mode = 2\nsize_flags_vertical = 3\n\n[node name=\"Editor\" type=\"CodeEdit\" parent=\"VBoxContainer/EdiorLayout\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\n\n[node name=\"MarginContainer\" type=\"MarginContainer\" parent=\"VBoxContainer/EdiorLayout/Editor\"]\nlayout_mode = 1\nanchors_preset = 12\nanchor_top = 1.0\nanchor_right = 1.0\nanchor_bottom = 1.0\noffset_top = -31.0\ngrow_horizontal = 2\ngrow_vertical = 0\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\n\n[node name=\"HBoxContainer\" type=\"HBoxContainer\" parent=\"VBoxContainer/EdiorLayout/Editor/MarginContainer\"]\nlayout_mode = 2\nsize_flags_vertical = 8\nalignment = 2\n\n[node name=\"Tags\" type=\"Button\" parent=\"VBoxContainer/EdiorLayout/Editor/MarginContainer/HBoxContainer\"]\nlayout_mode = 2\ntooltip_text = \"Shows supported tags.\"\ntext = \"Supported Tags\"\n\n[node name=\"SelectType\" type=\"OptionButton\" parent=\"VBoxContainer/EdiorLayout/Editor/MarginContainer/HBoxContainer\"]\nlayout_mode = 2\ntooltip_text = \"Select the script type specific template.\"\nitem_count = 2\nselected = 0\npopup/item_0/text = \"GD - GDScript\"\npopup/item_0/id = 1000\npopup/item_1/text = \"C# - CSharpScript\"\npopup/item_1/id = 2000\n\n[node name=\"Panel\" type=\"MarginContainer\" parent=\"VBoxContainer\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\n\n[node name=\"HBoxContainer\" type=\"HBoxContainer\" parent=\"VBoxContainer/Panel\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\nalignment = 2\n\n[node name=\"Restore\" type=\"Button\" parent=\"VBoxContainer/Panel/HBoxContainer\"]\nlayout_mode = 2\ntext = \"Restore\"\n\n[node name=\"Save\" type=\"Button\" parent=\"VBoxContainer/Panel/HBoxContainer\"]\nlayout_mode = 2\ndisabled = true\ntext = \"Save\"\n\n[node name=\"Tags\" type=\"PopupPanel\" parent=\".\"]\nsize = Vector2i(300, 100)\nunresizable = false\ncontent_scale_aspect = 4\n\n[node name=\"MarginContainer\" type=\"MarginContainer\" parent=\"Tags\"]\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\noffset_left = 4.0\noffset_top = 4.0\noffset_right = -856.0\noffset_bottom = -552.0\ngrow_horizontal = 2\ngrow_vertical = 2\n\n[node name=\"TextEdit\" type=\"CodeEdit\" parent=\"Tags/MarginContainer\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\neditable = false\ncontext_menu_enabled = false\nshortcut_keys_enabled = false\nvirtual_keyboard_enabled = false\n\n[connection signal=\"text_changed\" from=\"VBoxContainer/EdiorLayout/Editor\" to=\".\" method=\"_on_Editor_text_changed\"]\n[connection signal=\"pressed\" from=\"VBoxContainer/EdiorLayout/Editor/MarginContainer/HBoxContainer/Tags\" to=\".\" method=\"_on_Tags_pressed\"]\n[connection signal=\"item_selected\" from=\"VBoxContainer/EdiorLayout/Editor/MarginContainer/HBoxContainer/SelectType\" to=\".\" method=\"_on_SelectType_item_selected\"]\n[connection signal=\"pressed\" from=\"VBoxContainer/Panel/HBoxContainer/Restore\" to=\".\" method=\"_on_Restore_pressed\"]\n[connection signal=\"pressed\" from=\"VBoxContainer/Panel/HBoxContainer/Save\" to=\".\" method=\"_on_Save_pressed\"]\n"
  },
  {
    "path": "addons/gdUnit4/src/update/GdMarkDownReader.gd",
    "content": "@tool\nextends RefCounted\n\nconst GdUnitUpdateClient = preload(\"res://addons/gdUnit4/src/update/GdUnitUpdateClient.gd\")\n\nconst FONT_H1 := 22\nconst FONT_H2 := 20\nconst FONT_H3 := 18\nconst FONT_H4 := 16\nconst FONT_H5 := 14\nconst FONT_H6 := 12\n\nconst HORIZONTAL_RULE := \"[img=4000x2]res://addons/gdUnit4/src/update/assets/horizontal-line2.png[/img]\"\nconst HEADER_RULE := \"[font_size=%d]$1[/font_size]\"\nconst HEADER_CENTERED_RULE := \"[font_size=%d][center]$1[/center][/font_size]\"\n\nconst image_download_folder := \"res://addons/gdUnit4/tmp-update/\"\n\nconst exclude_font_size := \"\\b(?!(?:(font_size))\\b)\"\n\nvar md_replace_patterns := [\n\t# comments\n\t[regex(\"(?m)^\\\\n?\\\\s*<!--[\\\\s\\\\S]*?-->\\\\s*\\\\n?\"), \"\"],\n\n\t# horizontal rules\n\t[regex(\"(?m)^[ ]{0,3}---$\"), HORIZONTAL_RULE],\n\t[regex(\"(?m)^[ ]{0,3}___$\"), HORIZONTAL_RULE],\n\t[regex(\"(?m)^[ ]{0,3}\\\\*\\\\*\\\\*$\"), HORIZONTAL_RULE],\n\n\t# headers\n\t[regex(\"(?m)^###### (.*)\"), HEADER_RULE % FONT_H6],\n\t[regex(\"(?m)^##### (.*)\"), HEADER_RULE % FONT_H5],\n\t[regex(\"(?m)^#### (.*)\"), HEADER_RULE % FONT_H4],\n\t[regex(\"(?m)^### (.*)\"), HEADER_RULE % FONT_H3],\n\t[regex(\"(?m)^## (.*)\"), (HEADER_RULE + HORIZONTAL_RULE) % FONT_H2],\n\t[regex(\"(?m)^# (.*)\"), (HEADER_RULE + HORIZONTAL_RULE) % FONT_H1],\n\t[regex(\"(?m)^(.+)=={2,}$\"), HEADER_RULE % FONT_H1],\n\t[regex(\"(?m)^(.+)--{2,}$\"), HEADER_RULE % FONT_H2],\n\t# html headers\n\t[regex(\"<h1>((.*?\\\\R?)+)<\\\\/h1>\"), (HEADER_RULE + HORIZONTAL_RULE) % FONT_H1],\n\t[regex(\"<h1[ ]*align[ ]*=[ ]*\\\"center\\\">((.*?\\\\R?)+)<\\\\/h1>\"), (HEADER_CENTERED_RULE + HORIZONTAL_RULE) % FONT_H1],\n\t[regex(\"<h2>((.*?\\\\R?)+)<\\\\/h2>\"), (HEADER_RULE + HORIZONTAL_RULE) % FONT_H2],\n\t[regex(\"<h2[ ]*align[ ]*=[ ]*\\\"center\\\">((.*?\\\\R?)+)<\\\\/h2>\"), (HEADER_CENTERED_RULE + HORIZONTAL_RULE) % FONT_H1],\n\t[regex(\"<h3>((.*?\\\\R?)+)<\\\\/h3>\"), HEADER_RULE % FONT_H3],\n\t[regex(\"<h3[ ]*align[ ]*=[ ]*\\\"center\\\">((.*?\\\\R?)+)<\\\\/h3>\"), HEADER_CENTERED_RULE % FONT_H3],\n\t[regex(\"<h4>((.*?\\\\R?)+)<\\\\/h4>\"), HEADER_RULE % FONT_H4],\n\t[regex(\"<h4[ ]*align[ ]*=[ ]*\\\"center\\\">((.*?\\\\R?)+)<\\\\/h4>\"), HEADER_CENTERED_RULE % FONT_H4],\n\t[regex(\"<h5>((.*?\\\\R?)+)<\\\\/h5>\"), HEADER_RULE % FONT_H5],\n\t[regex(\"<h5[ ]*align[ ]*=[ ]*\\\"center\\\">((.*?\\\\R?)+)<\\\\/h5>\"), HEADER_CENTERED_RULE % FONT_H5],\n\t[regex(\"<h6>((.*?\\\\R?)+)<\\\\/h6>\"), HEADER_RULE % FONT_H6],\n\t[regex(\"<h6[ ]*align[ ]*=[ ]*\\\"center\\\">((.*?\\\\R?)+)<\\\\/h6>\"), HEADER_CENTERED_RULE % FONT_H6],\n\n\t# asterics\n\t#[regex(\"(\\\\*)\"), \"xxx$1xxx\"],\n\n\t# extract/compile image references\n\t[regex(\"!\\\\[(.*?)\\\\]\\\\[(.*?)\\\\]\"), process_image_references],\n\t# extract images with path and optional tool tip\n\t[regex(\"!\\\\[(.*?)\\\\]\\\\((.*?)(( )+(.*?))?\\\\)\"), process_image],\n\n\t# links\n\t[regex(\"([!]|)\\\\[(.+)\\\\]\\\\(([^ ]+?)\\\\)\"),  \"[url={\\\"url\\\":\\\"$3\\\"}]$2[/url]\"],\n\t# links with tool tip\n\t[regex(\"([!]|)\\\\[(.+)\\\\]\\\\(([^ ]+?)( \\\"(.+)\\\")?\\\\)\"),  \"[url={\\\"url\\\":\\\"$3\\\", \\\"tool_tip\\\":\\\"$5\\\"}]$2[/url]\"],\n\t# links to github, as shorted link\n\t[regex(\"(https://github.*/?/(\\\\S+))\"), '[url={\"url\":\"$1\", \"tool_tip\":\"$1\"}]#$2[/url]'],\n\n\t# embeded text\n\t[regex(\"(?m)^[ ]{0,3}>(.*?)$\"), \"[img=50x14]res://addons/gdUnit4/src/update/assets/embedded.png[/img][i]$1[/i]\"],\n\n\t# italic + bold font\n\t[regex(\"[_]{3}(.*?)[_]{3}\"), \"[i][b]$1[/b][/i]\"],\n\t[regex(\"[\\\\*]{3}(.*?)[\\\\*]{3}\"), \"[i][b]$1[/b][/i]\"],\n\t# bold font\n\t[regex(\"<b>(.*?)<\\\\/b>\"), \"[b]$1[/b]\"],\n\t[regex(\"[_]{2}(.*?)[_]{2}\"), \"[b]$1[/b]\"],\n\t[regex(\"[\\\\*]{2}(.*?)[\\\\*]{2}\"), \"[b]$1[/b]\"],\n\t# italic font\n\t[regex(\"<i>(.*?)<\\\\/i>\"), \"[i]$1[/i]\"],\n\t[regex(exclude_font_size+\"_(.*?)_\"), \"[i]$1[/i]\"],\n\t[regex(\"\\\\*(.*?)\\\\*\"), \"[i]$1[/i]\"],\n\n\t# strikethrough font\n\t[regex(\"<s>(.*?)</s>\"), \"[s]$1[/s]\"],\n\t[regex(\"~~(.*?)~~\"), \"[s]$1[/s]\"],\n\t[regex(\"~(.*?)~\"), \"[s]$1[/s]\"],\n\n\t# handling lists\n\t# using an image for dots\n\t[regex(\"(?m)^[ ]{0,1}[*\\\\-+] (.*)$\"), list_replace(0)],\n\t[regex(\"(?m)^[ ]{2,3}[*\\\\-+] (.*)$\"), list_replace(1)],\n\t[regex(\"(?m)^[ ]{4,5}[*\\\\-+] (.*)$\"), list_replace(2)],\n\t[regex(\"(?m)^[ ]{6,7}[*\\\\-+] (.*)$\"), list_replace(3)],\n\t[regex(\"(?m)^[ ]{8,9}[*\\\\-+] (.*)$\"), list_replace(4)],\n\n\t# code\n\t[regex(\"``([\\\\s\\\\S]*?)``\"), code_block(\"$1\")],\n\t[regex(\"`([\\\\s\\\\S]*?)`{1,2}\"), code_block(\"$1\")],\n]\n\nvar code_block_patterns := [\n\t# code blocks, code blocks looks not like code blocks in richtext\n\t[regex(\"```(javascript|python|shell|gdscript|gd)([\\\\s\\\\S]*?\\n)```\"), code_block(\"$2\", true)],\n]\n\nvar _img_replace_regex := RegEx.new()\nvar _image_urls := PackedStringArray()\nvar _on_table_tag := false\nvar _client: GdUnitUpdateClient\n\n\nstatic func regex(pattern: String) -> RegEx:\n\tvar regex_ := RegEx.new()\n\tvar err := regex_.compile(pattern)\n\tif err != OK:\n\t\tpush_error(\"error '%s' checked pattern '%s'\" % [err, pattern])\n\t\treturn null\n\treturn regex_\n\n\nfunc _init() -> void:\n\t@warning_ignore(\"return_value_discarded\")\n\t_img_replace_regex.compile(\"\\\\[img\\\\]((.*?))\\\\[/img\\\\]\")\n\n\nfunc set_http_client(client: GdUnitUpdateClient) -> void:\n\t_client = client\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc _notification(what: int) -> void:\n\tif what == NOTIFICATION_PREDELETE:\n\t\t# finally remove_at the downloaded images\n\t\tfor image in _image_urls:\n\t\t\tDirAccess.remove_absolute(image)\n\t\t\tDirAccess.remove_absolute(image + \".import\")\n\n\nfunc list_replace(indent: int) -> String:\n\tvar replace_pattern := \"[img=12x12]res://addons/gdUnit4/src/update/assets/dot2.png[/img]\" if indent %2 else \"[img=12x12]res://addons/gdUnit4/src/update/assets/dot1.png[/img]\"\n\treplace_pattern += \" $1\"\n\n\tfor index in indent:\n\t\treplace_pattern = replace_pattern.insert(0, \"   \")\n\treturn replace_pattern\n\n\nfunc code_block(replace: String, border: bool = false) -> String:\n\tif border:\n\t\treturn \"\"\"\n\t\t\t[img=1400x14]res://addons/gdUnit4/src/update/assets/border_top.png[/img]\n\t\t\t[indent][color=GRAY][font_size=16]%s[/font_size][/color][/indent]\n\t\t\t[img=1400x14]res://addons/gdUnit4/src/update/assets/border_bottom.png[/img]\n\t\t\t\"\"\".dedent() % replace\n\treturn \"[code][bgcolor=DARK_SLATE_GRAY][color=GRAY][font_size=16]%s[/font_size][/color][/bgcolor][/code]\" % replace\n\n\nfunc convert_text(input: String) -> String:\n\tinput = process_tables(input)\n\n\tfor pattern: Array in md_replace_patterns:\n\t\tvar regex_: RegEx = pattern[0]\n\t\tvar bb_replace: Variant = pattern[1]\n\t\tif bb_replace is Callable:\n\t\t\t@warning_ignore(\"unsafe_method_access\")\n\t\t\tinput = await bb_replace.call(regex_, input)\n\t\telse:\n\t\t\t@warning_ignore(\"unsafe_cast\")\n\t\t\tinput = regex_.sub(input, bb_replace as String, true)\n\treturn input\n\n\nfunc convert_code_block(input: String) -> String:\n\tfor pattern: Array in code_block_patterns:\n\t\tvar regex_: RegEx = pattern[0]\n\t\tvar bb_replace: Variant = pattern[1]\n\t\tif bb_replace is Callable:\n\t\t\t@warning_ignore(\"unsafe_method_access\")\n\t\t\tinput = await bb_replace.call(regex_, input)\n\t\telse:\n\t\t\t@warning_ignore(\"unsafe_cast\")\n\t\t\tinput = regex_.sub(input, bb_replace as String, true)\n\treturn input\n\n\nfunc to_bbcode(input: String) -> String:\n\tvar re := regex(\"(?m)```[\\\\s\\\\S]*?```\")\n\tvar current_pos := 0\n\tvar as_bbcode := \"\"\n\n\t# we split by code blocks to handle this blocks customized\n\tfor result in re.search_all(input):\n\t\t# Add text before code block\n\t\tif result.get_start() > current_pos:\n\t\t\tas_bbcode += await convert_text(input.substr(current_pos, result.get_start() - current_pos))\n\t\t# Add code block\n\t\tas_bbcode += await convert_code_block(result.get_string())\n\t\tcurrent_pos = result.get_end()\n\n\t# Add remaining text after last code block\n\tif current_pos < input.length():\n\t\tas_bbcode += await convert_text(input.substr(current_pos))\n\treturn as_bbcode\n\n\nfunc process_tables(input: String) -> String:\n\tvar bbcode := PackedStringArray()\n\tvar lines: Array[String] = Array(input.split(\"\\n\") as Array, TYPE_STRING, \"\", null)\n\twhile not lines.is_empty():\n\t\tif is_table(lines[0]):\n\t\t\tbbcode.append_array(parse_table(lines))\n\t\t\tcontinue\n\t\t@warning_ignore(\"return_value_discarded\", \"unsafe_cast\")\n\t\tbbcode.append(lines.pop_front() as String)\n\treturn \"\\n\".join(bbcode)\n\n\nclass Table:\n\tvar _columns: int\n\tvar _rows: Array[Row] = []\n\n\tclass Row:\n\t\tvar _cells := PackedStringArray()\n\n\n\t\tfunc _init(cells: PackedStringArray, columns: int) -> void:\n\t\t\t_cells = cells\n\t\t\tfor i in range(_cells.size(), columns):\n\t\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t\t_cells.append(\"\")\n\n\n\t\tfunc to_bbcode(cell_sizes: PackedInt32Array, bold: bool) -> String:\n\t\t\tvar cells := PackedStringArray()\n\t\t\tfor cell_index in _cells.size():\n\t\t\t\tvar cell: String = _cells[cell_index]\n\t\t\t\tif cell.strip_edges() == \"--\":\n\t\t\t\t\tcell = create_line(cell_sizes[cell_index])\n\t\t\t\tif bold:\n\t\t\t\t\tcell = \"[b]%s[/b]\" % cell\n\t\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t\tcells.append(\"[cell]%s[/cell]\" % cell)\n\t\t\treturn \"|\".join(cells)\n\n\n\t\tfunc create_line(length: int) -> String:\n\t\t\tvar line := \"\"\n\t\t\tfor i in length:\n\t\t\t\tline += \"-\"\n\t\t\treturn line\n\n\n\tfunc _init(columns: int) -> void:\n\t\t_columns = columns\n\n\n\tfunc parse_row(line :String) -> bool:\n\t\t# is line containing cells?\n\t\tif line.find(\"|\") == -1:\n\t\t\treturn false\n\t\t_rows.append(Row.new(line.split(\"|\"), _columns))\n\t\treturn true\n\n\n\tfunc calculate_max_cell_sizes() -> PackedInt32Array:\n\t\tvar cells_size := PackedInt32Array()\n\t\tfor column in _columns:\n\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\tcells_size.append(0)\n\n\t\tfor row_index in _rows.size():\n\t\t\tvar row: Row = _rows[row_index]\n\t\t\tfor cell_index in row._cells.size():\n\t\t\t\tvar cell_size: int = cells_size[cell_index]\n\t\t\t\tvar size := row._cells[cell_index].length()\n\t\t\t\tif size > cell_size:\n\t\t\t\t\tcells_size[cell_index] = size\n\t\treturn cells_size\n\n\n\t@warning_ignore(\"return_value_discarded\")\n\tfunc to_bbcode() -> PackedStringArray:\n\t\tvar cell_sizes := calculate_max_cell_sizes()\n\t\tvar bb_code := PackedStringArray()\n\n\t\tbb_code.append(\"[table=%d]\" % _columns)\n\t\tfor row_index in _rows.size():\n\t\t\tbb_code.append(_rows[row_index].to_bbcode(cell_sizes, row_index==0))\n\t\tbb_code.append(\"[/table]\\n\")\n\t\treturn bb_code\n\n\nfunc parse_table(lines: Array) -> PackedStringArray:\n\tvar line: String = lines[0]\n\tvar table := Table.new(line.count(\"|\") + 1)\n\twhile not lines.is_empty():\n\t\tline = lines.pop_front()\n\t\tif not table.parse_row(line):\n\t\t\tbreak\n\treturn table.to_bbcode()\n\n\nfunc is_table(line: String) -> bool:\n\treturn line.find(\"|\") != -1\n\n\nfunc open_table(line: String) -> String:\n\t_on_table_tag = true\n\treturn \"[table=%d]\" % (line.count(\"|\") + 1)\n\n\nfunc close_table() -> String:\n\t_on_table_tag = false\n\treturn \"[/table]\"\n\n\nfunc extract_cells(line: String, bold := false) -> String:\n\tvar cells := \"\"\n\tfor cell in line.split(\"|\"):\n\t\tif bold:\n\t\t\tcell = \"[b]%s[/b]\" % cell\n\t\tcells += \"[cell]%s[/cell]\" % cell\n\treturn cells\n\n\nfunc process_image_references(p_regex: RegEx, p_input: String) -> String:\n\t#return p_input\n\n\t# exists references?\n\tvar matches := p_regex.search_all(p_input)\n\tif matches.is_empty():\n\t\treturn p_input\n\t# collect image references and remove_at it\n\tvar references := Dictionary()\n\tvar link_regex := regex(\"\\\\[(\\\\S+)\\\\]:(\\\\S+)([ ]\\\"(.*)\\\")?\")\n\t# create copy of original source to replace checked it\n\tvar input := p_input.replace(\"\\r\", \"\")\n\tvar extracted_references :=  p_input.replace(\"\\r\", \"\")\n\tfor reg_match in link_regex.search_all(input):\n\t\tvar line := reg_match.get_string(0) + \"\\n\"\n\t\tvar ref := reg_match.get_string(1)\n\t\t#var topl_tip = reg_match.get_string(4)\n\t\t# collect reference and url\n\t\treferences[ref] = reg_match.get_string(2)\n\t\textracted_references = extracted_references.replace(line, \"\")\n\n\t# replace image references by collected url's\n\tfor reference_key: String in references.keys():\n\t\tvar regex_key := regex(\"\\\\](\\\\[%s\\\\])\" % reference_key)\n\t\tfor reg_match in regex_key.search_all(extracted_references):\n\t\t\tvar ref: String = reg_match.get_string(0)\n\t\t\tvar image_url: String = \"](%s)\" % references.get(reference_key)\n\t\t\textracted_references = extracted_references.replace(ref, image_url)\n\treturn extracted_references\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc process_image(p_regex: RegEx, p_input: String) -> String:\n\t#return p_input\n\tvar to_replace := PackedStringArray()\n\tvar tool_tips :=  PackedStringArray()\n\t# find all matches\n\tvar matches := p_regex.search_all(p_input)\n\tif matches.is_empty():\n\t\treturn p_input\n\tfor reg_match in matches:\n\t\t# grap the parts to replace and store temporay because a direct replace will distort the offsets\n\t\tto_replace.append(p_input.substr(reg_match.get_start(0), reg_match.get_end(0)))\n\t\t# grap optional tool tips\n\t\ttool_tips.append(reg_match.get_string(5))\n\t# finally replace all findings\n\tfor replace in to_replace:\n\t\tvar re := p_regex.sub(replace, \"[img]$2[/img]\")\n\t\tp_input = p_input.replace(replace, re)\n\treturn await _process_external_image_resources(p_input)\n\n\nfunc _process_external_image_resources(input: String) -> String:\n\t@warning_ignore(\"return_value_discarded\")\n\tDirAccess.make_dir_recursive_absolute(image_download_folder)\n\t# scan all img for external resources and download it\n\tfor value in _img_replace_regex.search_all(input):\n\t\tif value.get_group_count() >= 1:\n\t\t\tvar image_url: String = value.get_string(1)\n\t\t\t# if not a local resource we need to download it\n\t\t\tif image_url.begins_with(\"http\"):\n\t\t\t\tif OS.is_stdout_verbose():\n\t\t\t\t\tprints(\"download image:\", image_url)\n\t\t\t\tvar response := await _client.request_image(image_url)\n\t\t\t\tif response.status() == 200:\n\t\t\t\t\tvar image := Image.new()\n\t\t\t\t\tvar error := image.load_png_from_buffer(response.get_body())\n\t\t\t\t\tif error != OK:\n\t\t\t\t\t\tprints(\"Error creating image from response\", error)\n\t\t\t\t\t# replace characters where format characters\n\t\t\t\t\tvar new_url := image_download_folder + image_url.get_file().replace(\"_\", \"-\")\n\t\t\t\t\tif new_url.get_extension() != 'png':\n\t\t\t\t\t\tnew_url = new_url + '.png'\n\t\t\t\t\tvar err := image.save_png(new_url)\n\t\t\t\t\tif err:\n\t\t\t\t\t\tpush_error(\"Can't save image to '%s'. Error: %s\" % [new_url, error_string(err)])\n\t\t\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t\t\t_image_urls.append(new_url)\n\t\t\t\t\tinput = input.replace(image_url, new_url)\n\treturn input\n"
  },
  {
    "path": "addons/gdUnit4/src/update/GdMarkDownReader.gd.uid",
    "content": "uid://bcn1mkbu62t1e\n"
  },
  {
    "path": "addons/gdUnit4/src/update/GdUnitPatch.gd",
    "content": "class_name GdUnitPatch\nextends RefCounted\n\nconst PATCH_VERSION = \"patch_version\"\n\nvar _version :GdUnit4Version\n\n\nfunc _init(version_ :GdUnit4Version) -> void:\n\t_version = version_\n\n\nfunc version() -> GdUnit4Version:\n\treturn _version\n\n\n# this function needs to be implement\nfunc execute() -> bool:\n\tpush_error(\"The function 'execute()' is not implemented at %s\" % self)\n\treturn false\n"
  },
  {
    "path": "addons/gdUnit4/src/update/GdUnitPatch.gd.uid",
    "content": "uid://jontqixti0bx\n"
  },
  {
    "path": "addons/gdUnit4/src/update/GdUnitPatcher.gd",
    "content": "class_name GdUnitPatcher\nextends RefCounted\n\n\nconst _base_dir := \"res://addons/gdUnit4/src/update/patches/\"\n\nvar _patches := Dictionary()\n\n\nfunc scan(current :GdUnit4Version) -> void:\n\t_scan(_base_dir, current)\n\n\nfunc _scan(scan_path :String, current :GdUnit4Version) -> void:\n\t_patches = Dictionary()\n\tvar patch_paths := _collect_patch_versions(scan_path, current)\n\tfor path in patch_paths:\n\t\tprints(\"scan for patches checked '%s'\" % path)\n\t\t_patches[path] = _scan_patches(path)\n\n\nfunc patch_count() -> int:\n\tvar count := 0\n\tfor key :String in _patches.keys():\n\t\tcount += _patches[key].size()\n\treturn count\n\n\nfunc execute() -> void:\n\tfor key :String in _patches.keys():\n\t\tfor path :String in _patches[key]:\n\t\t\tvar patch :GdUnitPatch = load(key + \"/\" + path).new()\n\t\t\tif patch:\n\t\t\t\tprints(\"execute patch\", patch.version(), patch.get_script().resource_path)\n\t\t\t\tif not patch.execute():\n\t\t\t\t\tprints(\"error checked execution patch %s\" % key + \"/\" + path)\n\n\nfunc _collect_patch_versions(scan_path :String, current :GdUnit4Version) -> PackedStringArray:\n\tif not DirAccess.dir_exists_absolute(scan_path):\n\t\treturn PackedStringArray()\n\tvar patches := Array()\n\tvar dir := DirAccess.open(scan_path)\n\tif dir != null:\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tdir.list_dir_begin() # TODO GODOT4 fill missing arguments https://github.com/godotengine/godot/pull/40547\n\t\tvar next := \".\"\n\t\twhile next != \"\":\n\t\t\tnext = dir.get_next()\n\t\t\tif next.is_empty() or next == \".\" or next == \"..\":\n\t\t\t\tcontinue\n\t\t\tvar version := GdUnit4Version.parse(next)\n\t\t\tif version.is_greater(current):\n\t\t\t\tpatches.append(scan_path + next)\n\tpatches.sort()\n\treturn PackedStringArray(patches)\n\n\nfunc _scan_patches(path :String) -> PackedStringArray:\n\tvar patches := Array()\n\tvar dir := DirAccess.open(path)\n\tif dir != null:\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tdir.list_dir_begin() # TODOGODOT4 fill missing arguments https://github.com/godotengine/godot/pull/40547\n\t\tvar next := \".\"\n\t\twhile next != \"\":\n\t\t\tnext = dir.get_next()\n\t\t\t# step over directory links and .uid files\n\t\t\tif next.is_empty() or next == \".\" or next == \"..\" or next.ends_with(\".uid\"):\n\t\t\t\tcontinue\n\t\t\tpatches.append(next)\n\t# make sorted from lowest to high version\n\tpatches.sort()\n\treturn PackedStringArray(patches)\n"
  },
  {
    "path": "addons/gdUnit4/src/update/GdUnitPatcher.gd.uid",
    "content": "uid://b0fjvbxuuywvg\n"
  },
  {
    "path": "addons/gdUnit4/src/update/GdUnitUpdate.gd",
    "content": "@tool\nextends Container\n\nconst GdUnitTools := preload(\"res://addons/gdUnit4/src/core/GdUnitTools.gd\")\nconst GdUnitUpdateClient := preload(\"res://addons/gdUnit4/src/update/GdUnitUpdateClient.gd\")\nconst GDUNIT_TEMP := \"user://tmp\"\n\n@onready var _progress_content: RichTextLabel = %message\n@onready var _progress_bar: TextureProgressBar = %progress\n@onready var _cancel_btn: Button = %cancel\n@onready var _update_btn: Button = %update\n@onready var _spinner_img := GdUnitUiTools.get_spinner()\n\n\nvar _debug_mode := false\nvar _update_client :GdUnitUpdateClient\nvar _download_url :String\n\n\nfunc _ready() -> void:\n\tinit_progress(5)\n\n\nfunc _process(_delta :float) -> void:\n\tif _progress_content != null and _progress_content.is_visible_in_tree():\n\t\t_progress_content.queue_redraw()\n\n\nfunc init_progress(max_value: int) -> void:\n\t_cancel_btn.disabled = false\n\t_update_btn.disabled = false\n\t_progress_bar.max_value = max_value\n\t_progress_bar.value = 1\n\tmessage_h4(\"Press [Update] to start.\", Color.GREEN, false)\n\n\nfunc setup(update_client: GdUnitUpdateClient, download_url: String) -> void:\n\t_update_client = update_client\n\t_download_url = download_url\n\n\nfunc update_progress(message: String, color := Color.GREEN) -> void:\n\tmessage_h4(message, color)\n\t_progress_bar.value += 1\n\tif _debug_mode:\n\t\tawait get_tree().create_timer(3).timeout\n\tawait get_tree().create_timer(.2).timeout\n\n\nfunc _colored(message: String, color: Color) -> String:\n\treturn \"[color=#%s]%s[/color]\" % [color.to_html(), message]\n\n\nfunc message_h4(message: String, color: Color, show_spinner := true) -> void:\n\t_progress_content.clear()\n\tif show_spinner:\n\t\t_progress_content.add_image(_spinner_img)\n\t_progress_content.append_text(\" [font_size=16]%s[/font_size]\" % _colored(message, color))\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc run_update() -> void:\n\t_cancel_btn.disabled = true\n\t_update_btn.disabled = true\n\n\tawait update_progress(\"Downloading the update.\")\n\tawait download_release()\n\tawait update_progress(\"Extracting\")\n\tvar zip_file := temp_dir() + \"/update.zip\"\n\tvar tmp_path := create_temp_dir(\"update\")\n\tvar result :Variant = extract_zip(zip_file, tmp_path)\n\tif result == null:\n\t\tawait update_progress(\"Update failed! .. Rollback.\", Color.INDIAN_RED)\n\t\tawait get_tree().create_timer(3).timeout\n\t\t_cancel_btn.disabled = false\n\t\t_update_btn.disabled = false\n\t\tinit_progress(5)\n\t\thide()\n\t\treturn\n\n\tawait update_progress(\"Uninstall GdUnit4.\")\n\tdisable_gdUnit()\n\tif not _debug_mode:\n\t\tdelete_directory(\"res://addons/gdUnit4/\")\n\t# give editor time to react on deleted files\n\tawait get_tree().create_timer(1).timeout\n\n\tawait update_progress(\"Install new GdUnit4 version.\")\n\tif _debug_mode:\n\t\tcopy_directory(tmp_path, \"res://debug\")\n\telse:\n\t\tcopy_directory(tmp_path, \"res://\")\n\n\tawait update_progress(\"New GdUnit version successfully installed, Restarting Godot please wait.\")\n\tawait get_tree().create_timer(3).timeout\n\tenable_gdUnit()\n\thide()\n\tdelete_directory(\"res://addons/.gdunit_update\")\n\trestart_godot()\n\n\nfunc restart_godot() -> void:\n\tprints(\"Force restart Godot\")\n\tEditorInterface.restart_editor(true)\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc enable_gdUnit() -> void:\n\tvar enabled_plugins := PackedStringArray()\n\tif ProjectSettings.has_setting(\"editor_plugins/enabled\"):\n\t\tenabled_plugins = ProjectSettings.get_setting(\"editor_plugins/enabled\")\n\tif not enabled_plugins.has(\"res://addons/gdUnit4/plugin.cfg\"):\n\t\tenabled_plugins.append(\"res://addons/gdUnit4/plugin.cfg\")\n\tProjectSettings.set_setting(\"editor_plugins/enabled\", enabled_plugins)\n\tProjectSettings.save()\n\n\nfunc disable_gdUnit() -> void:\n\tEditorInterface.set_plugin_enabled(\"gdUnit4\", false)\n\n\nfunc temp_dir() -> String:\n\tif not DirAccess.dir_exists_absolute(GDUNIT_TEMP):\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tDirAccess.make_dir_recursive_absolute(GDUNIT_TEMP)\n\treturn GDUNIT_TEMP\n\n\nfunc create_temp_dir(folder_name :String) -> String:\n\tvar new_folder := temp_dir() + \"/\" + folder_name\n\tdelete_directory(new_folder)\n\tif not DirAccess.dir_exists_absolute(new_folder):\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tDirAccess.make_dir_recursive_absolute(new_folder)\n\treturn new_folder\n\n\nfunc delete_directory(path: String, only_content := false) -> void:\n\tvar dir := DirAccess.open(path)\n\tif dir != null:\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tdir.list_dir_begin()\n\t\tvar file_name := \".\"\n\t\twhile file_name != \"\":\n\t\t\tfile_name = dir.get_next()\n\t\t\tif file_name.is_empty() or file_name == \".\" or file_name == \"..\":\n\t\t\t\tcontinue\n\t\t\tvar next := path + \"/\" +file_name\n\t\t\tif dir.current_is_dir():\n\t\t\t\tdelete_directory(next)\n\t\t\telse:\n\t\t\t\t# delete file\n\t\t\t\tvar err := dir.remove(next)\n\t\t\t\tif err:\n\t\t\t\t\tprinterr(\"Delete %s failed: %s\" % [next, error_string(err)])\n\t\tif not only_content:\n\t\t\tvar err := dir.remove(path)\n\t\t\tif err:\n\t\t\t\tprinterr(\"Delete %s failed: %s\" % [path, error_string(err)])\n\n\nfunc copy_directory(from_dir: String, to_dir: String) -> bool:\n\tif not DirAccess.dir_exists_absolute(from_dir):\n\t\tprinterr(\"Source directory not found '%s'\" % from_dir)\n\t\treturn false\n\t# check if destination exists\n\tif not DirAccess.dir_exists_absolute(to_dir):\n\t\t# create it\n\t\tvar err := DirAccess.make_dir_recursive_absolute(to_dir)\n\t\tif err != OK:\n\t\t\tprinterr(\"Can't create directory '%s'. Error: %s\" % [to_dir, error_string(err)])\n\t\t\treturn false\n\tvar source_dir := DirAccess.open(from_dir)\n\tvar dest_dir := DirAccess.open(to_dir)\n\tif source_dir != null:\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tsource_dir.list_dir_begin()\n\t\tvar next := \".\"\n\n\t\twhile next != \"\":\n\t\t\tnext = source_dir.get_next()\n\t\t\tif next == \"\" or next == \".\" or next == \"..\":\n\t\t\t\tcontinue\n\t\t\tvar source := source_dir.get_current_dir() + \"/\" + next\n\t\t\tvar dest := dest_dir.get_current_dir() + \"/\" + next\n\t\t\tif source_dir.current_is_dir():\n\t\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\t\tcopy_directory(source + \"/\", dest)\n\t\t\t\tcontinue\n\t\t\tvar err := source_dir.copy(source, dest)\n\t\t\tif err != OK:\n\t\t\t\tprinterr(\"Error checked copy file '%s' to '%s'\" % [source, dest])\n\t\t\t\treturn false\n\t\treturn true\n\telse:\n\t\tprinterr(\"Directory not found: \" + from_dir)\n\t\treturn false\n\n\nfunc extract_zip(zip_package: String, dest_path: String) -> Variant:\n\tvar zip: ZIPReader = ZIPReader.new()\n\tvar err := zip.open(zip_package)\n\tif err != OK:\n\t\tprinterr(\"Extracting `%s` failed! Please collect the error log and report this. Error Code: %s\" % [zip_package, err])\n\t\treturn null\n\tvar zip_entries: PackedStringArray = zip.get_files()\n\t# Get base path and step over archive folder\n\tvar archive_path := zip_entries[0]\n\tzip_entries.remove_at(0)\n\n\tfor zip_entry in zip_entries:\n\t\tvar new_file_path: String = dest_path + \"/\" + zip_entry.replace(archive_path, \"\")\n\t\tif zip_entry.ends_with(\"/\"):\n\t\t\t@warning_ignore(\"return_value_discarded\")\n\t\t\tDirAccess.make_dir_recursive_absolute(new_file_path)\n\t\t\tcontinue\n\t\tvar file: FileAccess = FileAccess.open(new_file_path, FileAccess.WRITE)\n\t\tfile.store_buffer(zip.read_file(zip_entry))\n\t@warning_ignore(\"return_value_discarded\")\n\tzip.close()\n\treturn dest_path\n\n\nfunc download_release() -> void:\n\tvar zip_file := GdUnitFileAccess.temp_dir() + \"/update.zip\"\n\tvar response :GdUnitUpdateClient.HttpResponse\n\tif _debug_mode:\n\t\tresponse = GdUnitUpdateClient.HttpResponse.new(200, PackedByteArray())\n\t\tzip_file = \"res://update.zip\"\n\t\treturn\n\n\tresponse = await _update_client.request_zip_package(_download_url, zip_file)\n\tif response.status() != 200:\n\t\tpush_warning(\"Update information cannot be retrieved from GitHub! \\n Error code: %d : %s\" % [response.status(), response.response()])\n\t\tmessage_h4(\"Download the update failed! Try it later again.\", Color.INDIAN_RED)\n\t\tawait get_tree().create_timer(3).timeout\n\n\nfunc _on_confirmed() -> void:\n\tawait run_update()\n\n\nfunc _on_cancel_pressed() -> void:\n\thide()\n\n\nfunc _on_update_pressed() -> void:\n\tawait run_update()\n"
  },
  {
    "path": "addons/gdUnit4/src/update/GdUnitUpdate.gd.uid",
    "content": "uid://baqn8kd41oqko\n"
  },
  {
    "path": "addons/gdUnit4/src/update/GdUnitUpdate.tscn",
    "content": "[gd_scene load_steps=6 format=3 uid=\"uid://2eahgaw88y6q\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/gdUnit4/src/update/GdUnitUpdate.gd\" id=\"1\"]\n\n[sub_resource type=\"Gradient\" id=\"Gradient_wilsr\"]\ncolors = PackedColorArray(0.151276, 0.151276, 0.151276, 1, 1, 1, 1, 1)\n\n[sub_resource type=\"GradientTexture2D\" id=\"GradientTexture2D_45cww\"]\ngradient = SubResource(\"Gradient_wilsr\")\nfill_to = Vector2(0.75641, 0)\n\n[sub_resource type=\"Gradient\" id=\"Gradient_i0qp8\"]\ncolors = PackedColorArray(1, 1, 1, 1, 0.20871, 0.20871, 0.20871, 1)\n\n[sub_resource type=\"GradientTexture2D\" id=\"GradientTexture2D_wilsr\"]\ngradient = SubResource(\"Gradient_i0qp8\")\nfill_from = Vector2(0.794872, 0)\nfill_to = Vector2(0, 0)\n\n[node name=\"GdUnitUpdate\" type=\"MarginContainer\"]\nclip_contents = true\ncustom_minimum_size = Vector2(0, 80)\nanchors_preset = 10\nanchor_right = 1.0\noffset_bottom = 80.0\ngrow_horizontal = 2\nsize_flags_horizontal = 3\ntheme_override_constants/margin_left = 10\ntheme_override_constants/margin_right = 10\nscript = ExtResource(\"1\")\n\n[node name=\"VBoxContainer\" type=\"VBoxContainer\" parent=\".\"]\nlayout_mode = 2\n\n[node name=\"Panel\" type=\"Panel\" parent=\"VBoxContainer\"]\nlayout_mode = 2\nsize_flags_vertical = 3\n\n[node name=\"message\" type=\"RichTextLabel\" parent=\"VBoxContainer/Panel\"]\nunique_name_in_owner = true\nlayout_mode = 1\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\nbbcode_enabled = true\ntext = \"aaaaa\"\nfit_content = true\nscroll_active = false\nshortcut_keys_enabled = false\n\n[node name=\"Panel2\" type=\"Panel\" parent=\"VBoxContainer\"]\nlayout_mode = 2\nsize_flags_vertical = 3\n\n[node name=\"progress\" type=\"TextureProgressBar\" parent=\"VBoxContainer/Panel2\"]\nunique_name_in_owner = true\nauto_translate_mode = 2\nclip_contents = true\ncustom_minimum_size = Vector2(0, 20)\nlayout_mode = 1\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\nlocalize_numeral_system = false\nmin_value = 1.0\nmax_value = 5.0\nvalue = 1.0\nrounded = true\nallow_greater = true\nnine_patch_stretch = true\ntexture_under = SubResource(\"GradientTexture2D_45cww\")\ntexture_progress = SubResource(\"GradientTexture2D_wilsr\")\ntint_under = Color(0.0235294, 0.145098, 0.168627, 1)\ntint_progress = Color(0.288912, 0.233442, 0.533772, 1)\n\n[node name=\"PanelContainer\" type=\"MarginContainer\" parent=\"VBoxContainer\"]\nlayout_mode = 2\nsize_flags_vertical = 3\n\n[node name=\"HBoxContainer\" type=\"HBoxContainer\" parent=\"VBoxContainer/PanelContainer\"]\nlayout_mode = 2\ntheme_override_constants/separation = 10\nalignment = 2\n\n[node name=\"update\" type=\"Button\" parent=\"VBoxContainer/PanelContainer/HBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntext = \"Update\"\n\n[node name=\"cancel\" type=\"Button\" parent=\"VBoxContainer/PanelContainer/HBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntext = \"Cancel\"\n\n[connection signal=\"pressed\" from=\"VBoxContainer/PanelContainer/HBoxContainer/update\" to=\".\" method=\"_on_update_pressed\"]\n[connection signal=\"pressed\" from=\"VBoxContainer/PanelContainer/HBoxContainer/cancel\" to=\".\" method=\"_on_cancel_pressed\"]\n"
  },
  {
    "path": "addons/gdUnit4/src/update/GdUnitUpdateClient.gd",
    "content": "@tool\nextends Node\n\nsignal request_completed(response: HttpResponse)\n\nclass HttpResponse:\n\tvar _http_status: int\n\tvar _body: PackedByteArray\n\n\n\tfunc _init(http_status: int, body: PackedByteArray) -> void:\n\t\t_http_status = http_status\n\t\t_body = body\n\n\n\tfunc status() -> int:\n\t\treturn _http_status\n\n\n\tfunc response() -> Variant:\n\t\tif _http_status != 200:\n\t\t\treturn _body.get_string_from_utf8()\n\n\t\tvar test_json_conv := JSON.new()\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tvar error := test_json_conv.parse(_body.get_string_from_utf8())\n\t\tif error != OK:\n\t\t\treturn \"HttpResponse: %s Error: %s\" % [error_string(error), _body.get_string_from_utf8()]\n\t\treturn test_json_conv.get_data()\n\n\tfunc get_body() -> PackedByteArray:\n\t\treturn _body\n\n\nvar _http_request := HTTPRequest.new()\n\n\nfunc _ready() -> void:\n\tadd_child(_http_request)\n\t@warning_ignore(\"return_value_discarded\")\n\t_http_request.request_completed.connect(_on_request_completed)\n\n\nfunc _notification(what: int) -> void:\n\tif what == NOTIFICATION_PREDELETE:\n\t\tif is_instance_valid(_http_request):\n\t\t\t_http_request.queue_free()\n\n\n#func list_tags() -> void:\n#\t_http_request.connect(\"request_completed\",Callable(self,\"_response_request_tags\"))\n#\tvar error = _http_request.request(\"https://api.github.com/repos/MikeSchulze/gdUnit4/tags\")\n#\tif error != OK:\n#\t\tpush_error(\"An error occurred in the HTTP request.\")\n\n\nfunc request_latest_version() -> HttpResponse:\n\tvar error := _http_request.request(\"https://api.github.com/repos/MikeSchulze/gdUnit4/tags\")\n\tif error != OK:\n\t\tvar message := \"Request latest version failed, %s\" % error_string(error)\n\t\treturn HttpResponse.new(error, message.to_utf8_buffer())\n\treturn await self.request_completed\n\n\nfunc request_releases() -> HttpResponse:\n\tvar error := _http_request.request(\"https://api.github.com/repos/MikeSchulze/gdUnit4/releases\")\n\tif error != OK:\n\t\tvar message := \"request_releases failed: %d\" % error\n\t\treturn HttpResponse.new(error, message.to_utf8_buffer())\n\treturn await self.request_completed\n\n\nfunc request_image(url: String) -> HttpResponse:\n\tvar error := _http_request.request(url)\n\tif error != OK:\n\t\tvar message := \"request_image failed: %d\" % error\n\t\treturn HttpResponse.new(error, message.to_utf8_buffer())\n\treturn await self.request_completed\n\n\nfunc request_zip_package(url: String, file: String) -> HttpResponse:\n\t_http_request.set_download_file(file)\n\tvar error := _http_request.request(url)\n\tif error != OK:\n\t\tvar message := \"request_zip_package failed: %d\" % error\n\t\treturn HttpResponse.new(error, message.to_utf8_buffer())\n\treturn await self.request_completed\n\n\nfunc extract_latest_version(response: HttpResponse) -> GdUnit4Version:\n\tvar body: Array = response.response()\n\treturn GdUnit4Version.parse(str(body[0][\"name\"]))\n\n\nfunc _on_request_completed(_result: int, response_http_status: int, _headers: PackedStringArray, body: PackedByteArray) -> void:\n\tif _http_request.get_http_client_status() != HTTPClient.STATUS_DISCONNECTED:\n\t\t_http_request.set_download_file(\"\")\n\trequest_completed.emit(HttpResponse.new(response_http_status, body))\n"
  },
  {
    "path": "addons/gdUnit4/src/update/GdUnitUpdateClient.gd.uid",
    "content": "uid://bktq2igb3bqom\n"
  },
  {
    "path": "addons/gdUnit4/src/update/GdUnitUpdateNotify.gd",
    "content": "@tool\nextends MarginContainer\n\n#signal request_completed(response)\n\nconst GdMarkDownReader = preload(\"res://addons/gdUnit4/src/update/GdMarkDownReader.gd\")\nconst GdUnitUpdateClient = preload(\"res://addons/gdUnit4/src/update/GdUnitUpdateClient.gd\")\nconst GdUnitUpdateProgress = preload(\"res://addons/gdUnit4/src/update/GdUnitUpdate.gd\")\n\n@onready var _md_reader: GdMarkDownReader = GdMarkDownReader.new()\n@onready var _update_client: GdUnitUpdateClient = $GdUnitUpdateClient\n@onready var _header: Label = $Panel/GridContainer/PanelContainer/header\n@onready var _update_button: Button = $Panel/GridContainer/Panel/HBoxContainer/update\n@onready var _content: RichTextLabel = $Panel/GridContainer/PanelContainer2/ScrollContainer/MarginContainer/content\n@onready var _update_progress :GdUnitUpdateProgress = %update_banner\n\nvar _debug_mode := false\nvar _patcher := GdUnitPatcher.new()\nvar _current_version := GdUnit4Version.current()\n\n\nfunc _ready() -> void:\n\t_update_button.set_disabled(false)\n\t_md_reader.set_http_client(_update_client)\n\t@warning_ignore(\"return_value_discarded\")\n\t#GdUnitFonts.init_fonts(_content)\n\t_update_progress.set_visible(false)\n\t_update_progress.hidden.connect(func() -> void:\n\t\t_update_button.set_disabled(false)\n\t)\n\n\nfunc request_releases() -> bool:\n\tif _debug_mode:\n\t\t_update_progress._debug_mode = _debug_mode\n\t\t_header.text = \"A new version 'v4.4.4' is available\"\n\t\t_update_button.set_disabled(false)\n\t\treturn true\n\n\tvar response :GdUnitUpdateClient.HttpResponse = await _update_client.request_latest_version()\n\tif response.status() != 200:\n\t\t_header.text = \"Update information cannot be retrieved from GitHub!\"\n\t\tmessage_h4(\"\\n\\nError: %s\" % response.response(), Color.INDIAN_RED)\n\t\treturn false\n\tvar latest_version := _update_client.extract_latest_version(response)\n\t# if same version exit here no update need\n\tif latest_version.is_greater(_current_version):\n\t\t_patcher.scan(_current_version)\n\t\t_header.text = \"A new version '%s' is available\" % latest_version\n\t\tvar download_zip_url := extract_zip_url(response)\n\t\t_update_progress.setup(_update_client, download_zip_url)\n\t\t_update_button.set_disabled(false)\n\t\treturn true\n\telse:\n\t\t_header.text = \"No update is available.\"\n\t\t_update_button.set_disabled(true)\n\t\treturn false\n\n\nfunc _colored(message_: String, color: Color) -> String:\n\treturn \"[color=#%s]%s[/color]\" % [color.to_html(), message_]\n\n\nfunc message_h4(message_: String, color: Color, clear := true) -> void:\n\tif clear:\n\t\t_content.clear()\n\t_content.append_text(\"[font_size=16]%s[/font_size]\" % _colored(message_, color))\n\n\nfunc message(message_: String, color: Color) -> void:\n\t_content.clear()\n\t_content.append_text(_colored(message_, color))\n\n\nfunc _process(_delta: float) -> void:\n\tif _content != null and _content.is_visible_in_tree():\n\t\t_content.queue_redraw()\n\n\nfunc show_update() -> void:\n\tif not GdUnitSettings.is_update_notification_enabled():\n\t\t_header.text = \"No update is available.\"\n\t\tmessage_h4(\"The search for updates is deactivated.\", Color.CORNFLOWER_BLUE)\n\t\t_update_button.set_disabled(true)\n\t\treturn\n\n\tif not await request_releases():\n\t\treturn\n\t_update_button.set_disabled(true)\n\n\tprints(\"Scan for GdUnit4 Update ...\")\n\tmessage_h4(\"\\n\\n\\nRequest release infos ... \", Color.SNOW)\n\t_content.add_image(GdUnitUiTools.get_spinner(), 32, 32)\n\n\tvar content: String\n\tif _debug_mode:\n\t\tawait get_tree().create_timer(.2).timeout\n\t\tvar template := FileAccess.open(\"res://addons/gdUnit4/test/update/resources/http_response_releases.txt\", FileAccess.READ).get_as_text()\n\t\tcontent = await _md_reader.to_bbcode(template)\n\telse:\n\t\tvar response :GdUnitUpdateClient.HttpResponse = await _update_client.request_releases()\n\t\tif response.status() == 200:\n\t\t\tcontent = await extract_releases(response, _current_version)\n\t\telse:\n\t\t\tmessage_h4(\"\\n\\n\\nError checked request available releases!\", Color.INDIAN_RED)\n\t\t\treturn\n\n\t# finally force rescan to import images as textures\n\tif Engine.is_editor_hint():\n\t\tawait rescan()\n\tmessage(content, Color.CADET_BLUE)\n\t_update_button.set_disabled(false)\n\n\n\nfunc extract_zip_url(response: GdUnitUpdateClient.HttpResponse) -> String:\n\tvar body :Array = response.response()\n\treturn body[0][\"zipball_url\"]\n\n\nfunc extract_releases(response: GdUnitUpdateClient.HttpResponse, current_version: GdUnit4Version) -> String:\n\tawait get_tree().process_frame\n\tvar result := \"\"\n\tfor release :Dictionary in response.response():\n\t\tvar release_version := str(release[\"tag_name\"])\n\t\tif GdUnit4Version.parse(release_version).equals(current_version):\n\t\t\tbreak\n\t\tvar release_description := _colored(\"<h1>GdUnit Release %s</h1>\" % release_version, Color.CORNFLOWER_BLUE)\n\t\trelease_description += \"\\n\"\n\t\trelease_description += release[\"body\"]\n\t\trelease_description += \"\\n\\n\"\n\t\tresult += await _md_reader.to_bbcode(release_description)\n\treturn result\n\n\nfunc rescan() -> void:\n\tif Engine.is_editor_hint():\n\t\tif OS.is_stdout_verbose():\n\t\t\tprints(\".. reimport release resources\")\n\t\tvar fs := EditorInterface.get_resource_filesystem()\n\t\tfs.scan()\n\t\twhile fs.is_scanning():\n\t\t\tif OS.is_stdout_verbose():\n\t\t\t\tprogressBar(fs.get_scanning_progress() * 100 as int)\n\t\t\tawait get_tree().process_frame\n\t\tawait get_tree().process_frame\n\tawait get_tree().create_timer(1).timeout\n\n\nfunc progressBar(p_progress: int) -> void:\n\tif p_progress < 0:\n\t\tp_progress = 0\n\tif p_progress > 100:\n\t\tp_progress = 100\n\tprintraw(\"scan [%-50s] %-3d%%\\r\" % [\"\".lpad(int(p_progress/2.0), \"#\").rpad(50, \"-\"), p_progress])\n\n\n@warning_ignore(\"return_value_discarded\")\nfunc _on_update_pressed() -> void:\n\t_update_button.set_disabled(true)\n\t# close all opend scripts before start the update\n\tif not _debug_mode:\n\t\tScriptEditorControls.close_open_editor_scripts()\n\t# copy update source to a temp because the update is deleting the whole gdUnit folder\n\tDirAccess.make_dir_absolute(\"res://addons/.gdunit_update\")\n\tDirAccess.copy_absolute(\"res://addons/gdUnit4/src/update/GdUnitUpdate.tscn\", \"res://addons/.gdunit_update/GdUnitUpdate.tscn\")\n\tDirAccess.copy_absolute(\"res://addons/gdUnit4/src/update/GdUnitUpdate.gd\", \"res://addons/.gdunit_update/GdUnitUpdate.gd\")\n\tvar source := FileAccess.open(\"res://addons/gdUnit4/src/update/GdUnitUpdate.tscn\", FileAccess.READ)\n\tvar content := source.get_as_text().replace(\"res://addons/gdUnit4/src/update/GdUnitUpdate.gd\", \"res://addons/.gdunit_update/GdUnitUpdate.gd\")\n\tvar dest := FileAccess.open(\"res://addons/.gdunit_update/GdUnitUpdate.tscn\", FileAccess.WRITE)\n\tdest.store_string(content)\n\t_update_progress.set_visible(true)\n\n\nfunc _on_show_next_toggled(enabled: bool) -> void:\n\tGdUnitSettings.set_update_notification(enabled)\n\n\nfunc _on_cancel_pressed() -> void:\n\thide()\n\n\nfunc _on_content_meta_clicked(meta: String) -> void:\n\tvar properties: Dictionary = str_to_var(meta)\n\tif properties.has(\"url\"):\n\t\t@warning_ignore(\"return_value_discarded\")\n\t\tOS.shell_open(str(properties.get(\"url\")))\n\n\nfunc _on_content_meta_hover_started(meta: String) -> void:\n\tvar properties: Dictionary = str_to_var(meta)\n\tif properties.has(\"tool_tip\"):\n\t\t_content.set_tooltip_text(str(properties.get(\"tool_tip\")))\n\n\n@warning_ignore(\"unused_parameter\")\nfunc _on_content_meta_hover_ended(meta: String) -> void:\n\t_content.set_tooltip_text(\"\")\n\n\nfunc _on_visibility_changed() -> void:\n\tif not is_visible_in_tree():\n\t\treturn\n\tif _update_progress != null:\n\t\t_update_progress.set_visible(false)\n\tawait show_update()\n"
  },
  {
    "path": "addons/gdUnit4/src/update/GdUnitUpdateNotify.gd.uid",
    "content": "uid://cxkha0alslo3l\n"
  },
  {
    "path": "addons/gdUnit4/src/update/GdUnitUpdateNotify.tscn",
    "content": "[gd_scene load_steps=4 format=3 uid=\"uid://0xyeci1tqebj\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/gdUnit4/src/update/GdUnitUpdateNotify.gd\" id=\"1_112wo\"]\n[ext_resource type=\"Script\" path=\"res://addons/gdUnit4/src/update/GdUnitUpdateClient.gd\" id=\"2_18asx\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://2eahgaw88y6q\" path=\"res://addons/gdUnit4/src/update/GdUnitUpdate.tscn\" id=\"3_x87h6\"]\n\n[node name=\"Control\" type=\"MarginContainer\"]\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\nscript = ExtResource(\"1_112wo\")\n\n[node name=\"GdUnitUpdateClient\" type=\"Node\" parent=\".\"]\nscript = ExtResource(\"2_18asx\")\n\n[node name=\"Panel\" type=\"Panel\" parent=\".\"]\nlayout_mode = 2\n\n[node name=\"GridContainer\" type=\"VBoxContainer\" parent=\"Panel\"]\nlayout_mode = 1\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\nalignment = 1\n\n[node name=\"PanelContainer\" type=\"MarginContainer\" parent=\"Panel/GridContainer\"]\nlayout_mode = 2\ntheme_override_constants/margin_left = 4\ntheme_override_constants/margin_top = 4\ntheme_override_constants/margin_right = 4\ntheme_override_constants/margin_bottom = 4\n\n[node name=\"header\" type=\"Label\" parent=\"Panel/GridContainer/PanelContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 9\n\n[node name=\"PanelContainer2\" type=\"PanelContainer\" parent=\"Panel/GridContainer\"]\nlayout_mode = 2\nsize_flags_vertical = 3\n\n[node name=\"ScrollContainer\" type=\"ScrollContainer\" parent=\"Panel/GridContainer/PanelContainer2\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\n\n[node name=\"MarginContainer\" type=\"MarginContainer\" parent=\"Panel/GridContainer/PanelContainer2/ScrollContainer\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\n\n[node name=\"content\" type=\"RichTextLabel\" parent=\"Panel/GridContainer/PanelContainer2/ScrollContainer/MarginContainer\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\nbbcode_enabled = true\n\n[node name=\"update_banner\" parent=\"Panel/GridContainer\" instance=ExtResource(\"3_x87h6\")]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\nsize_flags_horizontal = 1\nsize_flags_vertical = 8\n\n[node name=\"Panel\" type=\"MarginContainer\" parent=\"Panel/GridContainer\"]\nlayout_mode = 2\nsize_flags_vertical = 8\ntheme_override_constants/margin_left = 4\ntheme_override_constants/margin_top = 4\ntheme_override_constants/margin_right = 4\ntheme_override_constants/margin_bottom = 4\n\n[node name=\"HBoxContainer\" type=\"HBoxContainer\" parent=\"Panel/GridContainer/Panel\"]\nuse_parent_material = true\nlayout_mode = 2\ntheme_override_constants/separation = 4\n\n[node name=\"update\" type=\"Button\" parent=\"Panel/GridContainer/Panel/HBoxContainer\"]\ncustom_minimum_size = Vector2(100, 40)\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 4\ntext = \"Update\"\n\n[connection signal=\"visibility_changed\" from=\".\" to=\".\" method=\"_on_visibility_changed\"]\n[connection signal=\"meta_clicked\" from=\"Panel/GridContainer/PanelContainer2/ScrollContainer/MarginContainer/content\" to=\".\" method=\"_on_content_meta_clicked\"]\n[connection signal=\"meta_hover_ended\" from=\"Panel/GridContainer/PanelContainer2/ScrollContainer/MarginContainer/content\" to=\".\" method=\"_on_content_meta_hover_ended\"]\n[connection signal=\"meta_hover_started\" from=\"Panel/GridContainer/PanelContainer2/ScrollContainer/MarginContainer/content\" to=\".\" method=\"_on_content_meta_hover_started\"]\n[connection signal=\"pressed\" from=\"Panel/GridContainer/Panel/HBoxContainer/update\" to=\".\" method=\"_on_update_pressed\"]\n"
  },
  {
    "path": "addons/gdUnit4/src/update/assets/border_bottom.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://dmv3ld2otx1e2\"\npath=\"res://.godot/imported/border_bottom.png-30d66a4c67e3a03ad191e37cdf16549d.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/gdUnit4/src/update/assets/border_bottom.png\"\ndest_files=[\"res://.godot/imported/border_bottom.png-30d66a4c67e3a03ad191e37cdf16549d.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/gdUnit4/src/update/assets/border_top.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://b4sio0j5om50s\"\npath=\"res://.godot/imported/border_top.png-c47cbebdb755144731c6ae309e18bbaa.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/gdUnit4/src/update/assets/border_top.png\"\ndest_files=[\"res://.godot/imported/border_top.png-c47cbebdb755144731c6ae309e18bbaa.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/gdUnit4/src/update/assets/dot1.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://ce2eojg0pwpov\"\npath=\"res://.godot/imported/dot1.png-380baf1b5247addda93bce3c799aa4e7.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/gdUnit4/src/update/assets/dot1.png\"\ndest_files=[\"res://.godot/imported/dot1.png-380baf1b5247addda93bce3c799aa4e7.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/gdUnit4/src/update/assets/dot2.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cvwa5lg3qj0e2\"\npath=\"res://.godot/imported/dot2.png-86a9db80ef4413e353c4339ad8f68a5f.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/gdUnit4/src/update/assets/dot2.png\"\ndest_files=[\"res://.godot/imported/dot2.png-86a9db80ef4413e353c4339ad8f68a5f.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/gdUnit4/src/update/assets/embedded.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://63wk5nib3r7q\"\npath=\"res://.godot/imported/embedded.png-29390948772209a603567d24f8766495.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/gdUnit4/src/update/assets/embedded.png\"\ndest_files=[\"res://.godot/imported/embedded.png-29390948772209a603567d24f8766495.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/gdUnit4/src/update/assets/fonts/LICENSE.txt",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "addons/gdUnit4/src/update/assets/fonts/README.txt",
    "content": "Roboto Mono Variable Font\n=========================\n\nThis download contains Roboto Mono as both variable fonts and static fonts.\n\nRoboto Mono is a variable font with this axis:\n  wght\n\nThis means all the styles are contained in these files:\n  RobotoMono-VariableFont_wght.ttf\n  RobotoMono-Italic-VariableFont_wght.ttf\n\nIf your app fully supports variable fonts, you can now pick intermediate styles\nthat aren’t available as static fonts. Not all apps support variable fonts, and\nin those cases you can use the static font files for Roboto Mono:\n  static/RobotoMono-Thin.ttf\n  static/RobotoMono-ExtraLight.ttf\n  static/RobotoMono-Light.ttf\n  static/RobotoMono-Regular.ttf\n  static/RobotoMono-Medium.ttf\n  static/RobotoMono-SemiBold.ttf\n  static/RobotoMono-Bold.ttf\n  static/RobotoMono-ThinItalic.ttf\n  static/RobotoMono-ExtraLightItalic.ttf\n  static/RobotoMono-LightItalic.ttf\n  static/RobotoMono-Italic.ttf\n  static/RobotoMono-MediumItalic.ttf\n  static/RobotoMono-SemiBoldItalic.ttf\n  static/RobotoMono-BoldItalic.ttf\n\nGet started\n-----------\n\n1. Install the font files you want to use\n\n2. Use your app's font picker to view the font family and all the\navailable styles\n\nLearn more about variable fonts\n-------------------------------\n\n  https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts\n  https://variablefonts.typenetwork.com\n  https://medium.com/variable-fonts\n\nIn desktop apps\n\n  https://theblog.adobe.com/can-variable-fonts-illustrator-cc\n  https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts\n\nOnline\n\n  https://developers.google.com/fonts/docs/getting_started\n  https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide\n  https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts\n\nInstalling fonts\n\n  MacOS: https://support.apple.com/en-us/HT201749\n  Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux\n  Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows\n\nAndroid Apps\n\n  https://developers.google.com/fonts/docs/android\n  https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts\n\nLicense\n-------\nPlease read the full license text (LICENSE.txt) to understand the permissions,\nrestrictions and requirements for usage, redistribution, and modification.\n\nYou can use them freely in your products & projects - print or digital,\ncommercial or otherwise. However, you can't sell the fonts on their own.\n\nThis isn't legal advice, please consider consulting a lawyer and see the full\nlicense for all details.\n"
  },
  {
    "path": "addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Bold.ttf.import",
    "content": "[remap]\n\nimporter=\"font_data_dynamic\"\ntype=\"FontFile\"\nuid=\"uid://dgpj1y3a73vc\"\npath=\"res://.godot/imported/RobotoMono-Bold.ttf-ea008af97d359b7630bd271235703cae.fontdata\"\n\n[deps]\n\nsource_file=\"res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Bold.ttf\"\ndest_files=[\"res://.godot/imported/RobotoMono-Bold.ttf-ea008af97d359b7630bd271235703cae.fontdata\"]\n\n[params]\n\nRendering=null\nantialiasing=1\ngenerate_mipmaps=false\ndisable_embedded_bitmaps=true\nmultichannel_signed_distance_field=false\nmsdf_pixel_range=8\nmsdf_size=48\nallow_system_fallback=true\nforce_autohinter=false\nhinting=1\nsubpixel_positioning=1\noversampling=0.0\nFallbacks=null\nfallbacks=[]\nCompress=null\ncompress=true\npreload=[]\nlanguage_support={}\nscript_support={}\nopentype_features={}\n"
  },
  {
    "path": "addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-BoldItalic.ttf.import",
    "content": "[remap]\n\nimporter=\"font_data_dynamic\"\ntype=\"FontFile\"\nuid=\"uid://cnrsmyjdnlikm\"\npath=\"res://.godot/imported/RobotoMono-BoldItalic.ttf-6e10905211cda810d470782293480777.fontdata\"\n\n[deps]\n\nsource_file=\"res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-BoldItalic.ttf\"\ndest_files=[\"res://.godot/imported/RobotoMono-BoldItalic.ttf-6e10905211cda810d470782293480777.fontdata\"]\n\n[params]\n\nRendering=null\nantialiasing=1\ngenerate_mipmaps=false\ndisable_embedded_bitmaps=true\nmultichannel_signed_distance_field=false\nmsdf_pixel_range=8\nmsdf_size=48\nallow_system_fallback=true\nforce_autohinter=false\nhinting=1\nsubpixel_positioning=1\noversampling=0.0\nFallbacks=null\nfallbacks=[]\nCompress=null\ncompress=true\npreload=[]\nlanguage_support={}\nscript_support={}\nopentype_features={}\n"
  },
  {
    "path": "addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-ExtraLight.ttf.import",
    "content": "[remap]\n\nimporter=\"font_data_dynamic\"\ntype=\"FontFile\"\nuid=\"uid://c8ey5njg4eh6d\"\npath=\"res://.godot/imported/RobotoMono-ExtraLight.ttf-c8ac954f2ab584e7652e58ccd95cc705.fontdata\"\n\n[deps]\n\nsource_file=\"res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-ExtraLight.ttf\"\ndest_files=[\"res://.godot/imported/RobotoMono-ExtraLight.ttf-c8ac954f2ab584e7652e58ccd95cc705.fontdata\"]\n\n[params]\n\nRendering=null\nantialiasing=1\ngenerate_mipmaps=false\ndisable_embedded_bitmaps=true\nmultichannel_signed_distance_field=false\nmsdf_pixel_range=8\nmsdf_size=48\nallow_system_fallback=true\nforce_autohinter=false\nhinting=1\nsubpixel_positioning=1\noversampling=0.0\nFallbacks=null\nfallbacks=[]\nCompress=null\ncompress=true\npreload=[]\nlanguage_support={}\nscript_support={}\nopentype_features={}\n"
  },
  {
    "path": "addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-ExtraLightItalic.ttf.import",
    "content": "[remap]\n\nimporter=\"font_data_dynamic\"\ntype=\"FontFile\"\nuid=\"uid://pghouxn0ujr7\"\npath=\"res://.godot/imported/RobotoMono-ExtraLightItalic.ttf-06133dd8b521ead6203b317979387344.fontdata\"\n\n[deps]\n\nsource_file=\"res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-ExtraLightItalic.ttf\"\ndest_files=[\"res://.godot/imported/RobotoMono-ExtraLightItalic.ttf-06133dd8b521ead6203b317979387344.fontdata\"]\n\n[params]\n\nRendering=null\nantialiasing=1\ngenerate_mipmaps=false\ndisable_embedded_bitmaps=true\nmultichannel_signed_distance_field=false\nmsdf_pixel_range=8\nmsdf_size=48\nallow_system_fallback=true\nforce_autohinter=false\nhinting=1\nsubpixel_positioning=1\noversampling=0.0\nFallbacks=null\nfallbacks=[]\nCompress=null\ncompress=true\npreload=[]\nlanguage_support={}\nscript_support={}\nopentype_features={}\n"
  },
  {
    "path": "addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Italic.ttf.import",
    "content": "[remap]\n\nimporter=\"font_data_dynamic\"\ntype=\"FontFile\"\nuid=\"uid://hp3750m8e3nq\"\npath=\"res://.godot/imported/RobotoMono-Italic.ttf-328fe6d9b2ac5d629c43c335b916d307.fontdata\"\n\n[deps]\n\nsource_file=\"res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Italic.ttf\"\ndest_files=[\"res://.godot/imported/RobotoMono-Italic.ttf-328fe6d9b2ac5d629c43c335b916d307.fontdata\"]\n\n[params]\n\nRendering=null\nantialiasing=1\ngenerate_mipmaps=false\ndisable_embedded_bitmaps=true\nmultichannel_signed_distance_field=false\nmsdf_pixel_range=8\nmsdf_size=48\nallow_system_fallback=true\nforce_autohinter=false\nhinting=1\nsubpixel_positioning=1\noversampling=0.0\nFallbacks=null\nfallbacks=[]\nCompress=null\ncompress=true\npreload=[]\nlanguage_support={}\nscript_support={}\nopentype_features={}\n"
  },
  {
    "path": "addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Light.ttf.import",
    "content": "[remap]\n\nimporter=\"font_data_dynamic\"\ntype=\"FontFile\"\nuid=\"uid://dm1xiq8pmd6xk\"\npath=\"res://.godot/imported/RobotoMono-Light.ttf-638f745780c834176c3bf9969f0e408e.fontdata\"\n\n[deps]\n\nsource_file=\"res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Light.ttf\"\ndest_files=[\"res://.godot/imported/RobotoMono-Light.ttf-638f745780c834176c3bf9969f0e408e.fontdata\"]\n\n[params]\n\nRendering=null\nantialiasing=1\ngenerate_mipmaps=false\ndisable_embedded_bitmaps=true\nmultichannel_signed_distance_field=false\nmsdf_pixel_range=8\nmsdf_size=48\nallow_system_fallback=true\nforce_autohinter=false\nhinting=1\nsubpixel_positioning=1\noversampling=0.0\nFallbacks=null\nfallbacks=[]\nCompress=null\ncompress=true\npreload=[]\nlanguage_support={}\nscript_support={}\nopentype_features={}\n"
  },
  {
    "path": "addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-LightItalic.ttf.import",
    "content": "[remap]\n\nimporter=\"font_data_dynamic\"\ntype=\"FontFile\"\nuid=\"uid://crphq80yxhgic\"\npath=\"res://.godot/imported/RobotoMono-LightItalic.ttf-473f0d613e289d058b8c392d0c5242bc.fontdata\"\n\n[deps]\n\nsource_file=\"res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-LightItalic.ttf\"\ndest_files=[\"res://.godot/imported/RobotoMono-LightItalic.ttf-473f0d613e289d058b8c392d0c5242bc.fontdata\"]\n\n[params]\n\nRendering=null\nantialiasing=1\ngenerate_mipmaps=false\ndisable_embedded_bitmaps=true\nmultichannel_signed_distance_field=false\nmsdf_pixel_range=8\nmsdf_size=48\nallow_system_fallback=true\nforce_autohinter=false\nhinting=1\nsubpixel_positioning=1\noversampling=0.0\nFallbacks=null\nfallbacks=[]\nCompress=null\ncompress=true\npreload=[]\nlanguage_support={}\nscript_support={}\nopentype_features={}\n"
  },
  {
    "path": "addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Medium.ttf.import",
    "content": "[remap]\n\nimporter=\"font_data_dynamic\"\ntype=\"FontFile\"\nuid=\"uid://csdprnrpwr3xp\"\npath=\"res://.godot/imported/RobotoMono-Medium.ttf-f165ecef77d89557a95acac0927f13c4.fontdata\"\n\n[deps]\n\nsource_file=\"res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Medium.ttf\"\ndest_files=[\"res://.godot/imported/RobotoMono-Medium.ttf-f165ecef77d89557a95acac0927f13c4.fontdata\"]\n\n[params]\n\nRendering=null\nantialiasing=1\ngenerate_mipmaps=false\ndisable_embedded_bitmaps=true\nmultichannel_signed_distance_field=false\nmsdf_pixel_range=8\nmsdf_size=48\nallow_system_fallback=true\nforce_autohinter=false\nhinting=1\nsubpixel_positioning=1\noversampling=0.0\nFallbacks=null\nfallbacks=[]\nCompress=null\ncompress=true\npreload=[]\nlanguage_support={}\nscript_support={}\nopentype_features={}\n"
  },
  {
    "path": "addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-MediumItalic.ttf.import",
    "content": "[remap]\n\nimporter=\"font_data_dynamic\"\ntype=\"FontFile\"\nuid=\"uid://cdxcb7jq16o5k\"\npath=\"res://.godot/imported/RobotoMono-MediumItalic.ttf-40c40d791914284c8092585e839f0cd1.fontdata\"\n\n[deps]\n\nsource_file=\"res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-MediumItalic.ttf\"\ndest_files=[\"res://.godot/imported/RobotoMono-MediumItalic.ttf-40c40d791914284c8092585e839f0cd1.fontdata\"]\n\n[params]\n\nRendering=null\nantialiasing=1\ngenerate_mipmaps=false\ndisable_embedded_bitmaps=true\nmultichannel_signed_distance_field=false\nmsdf_pixel_range=8\nmsdf_size=48\nallow_system_fallback=true\nforce_autohinter=false\nhinting=1\nsubpixel_positioning=1\noversampling=0.0\nFallbacks=null\nfallbacks=[]\nCompress=null\ncompress=true\npreload=[]\nlanguage_support={}\nscript_support={}\nopentype_features={}\n"
  },
  {
    "path": "addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Regular.ttf.import",
    "content": "[remap]\n\nimporter=\"font_data_dynamic\"\ntype=\"FontFile\"\nuid=\"uid://ck2sp0iypcks\"\npath=\"res://.godot/imported/RobotoMono-Regular.ttf-f5a7315540116b55ba9e010120cbfb0c.fontdata\"\n\n[deps]\n\nsource_file=\"res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Regular.ttf\"\ndest_files=[\"res://.godot/imported/RobotoMono-Regular.ttf-f5a7315540116b55ba9e010120cbfb0c.fontdata\"]\n\n[params]\n\nRendering=null\nantialiasing=1\ngenerate_mipmaps=false\ndisable_embedded_bitmaps=true\nmultichannel_signed_distance_field=false\nmsdf_pixel_range=8\nmsdf_size=48\nallow_system_fallback=true\nforce_autohinter=false\nhinting=1\nsubpixel_positioning=1\noversampling=0.0\nFallbacks=null\nfallbacks=[]\nCompress=null\ncompress=true\npreload=[]\nlanguage_support={}\nscript_support={}\nopentype_features={}\n"
  },
  {
    "path": "addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-SemiBold.ttf.import",
    "content": "[remap]\n\nimporter=\"font_data_dynamic\"\ntype=\"FontFile\"\nuid=\"uid://bexpf232jnjbc\"\npath=\"res://.godot/imported/RobotoMono-SemiBold.ttf-6012d0b71d40b9767a7b6a480fe0d4b7.fontdata\"\n\n[deps]\n\nsource_file=\"res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-SemiBold.ttf\"\ndest_files=[\"res://.godot/imported/RobotoMono-SemiBold.ttf-6012d0b71d40b9767a7b6a480fe0d4b7.fontdata\"]\n\n[params]\n\nRendering=null\nantialiasing=1\ngenerate_mipmaps=false\ndisable_embedded_bitmaps=true\nmultichannel_signed_distance_field=false\nmsdf_pixel_range=8\nmsdf_size=48\nallow_system_fallback=true\nforce_autohinter=false\nhinting=1\nsubpixel_positioning=1\noversampling=0.0\nFallbacks=null\nfallbacks=[]\nCompress=null\ncompress=true\npreload=[]\nlanguage_support={}\nscript_support={}\nopentype_features={}\n"
  },
  {
    "path": "addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-SemiBoldItalic.ttf.import",
    "content": "[remap]\n\nimporter=\"font_data_dynamic\"\ntype=\"FontFile\"\nuid=\"uid://xuc8ovbe0rku\"\npath=\"res://.godot/imported/RobotoMono-SemiBoldItalic.ttf-12b525223c8f2dfb78bca4e7ecaf3ca5.fontdata\"\n\n[deps]\n\nsource_file=\"res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-SemiBoldItalic.ttf\"\ndest_files=[\"res://.godot/imported/RobotoMono-SemiBoldItalic.ttf-12b525223c8f2dfb78bca4e7ecaf3ca5.fontdata\"]\n\n[params]\n\nRendering=null\nantialiasing=1\ngenerate_mipmaps=false\ndisable_embedded_bitmaps=true\nmultichannel_signed_distance_field=false\nmsdf_pixel_range=8\nmsdf_size=48\nallow_system_fallback=true\nforce_autohinter=false\nhinting=1\nsubpixel_positioning=1\noversampling=0.0\nFallbacks=null\nfallbacks=[]\nCompress=null\ncompress=true\npreload=[]\nlanguage_support={}\nscript_support={}\nopentype_features={}\n"
  },
  {
    "path": "addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Thin.ttf.import",
    "content": "[remap]\n\nimporter=\"font_data_dynamic\"\ntype=\"FontFile\"\nuid=\"uid://cnc1pdajlxyxp\"\npath=\"res://.godot/imported/RobotoMono-Thin.ttf-a3a6620deea1a01e153a2a60c778e675.fontdata\"\n\n[deps]\n\nsource_file=\"res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Thin.ttf\"\ndest_files=[\"res://.godot/imported/RobotoMono-Thin.ttf-a3a6620deea1a01e153a2a60c778e675.fontdata\"]\n\n[params]\n\nRendering=null\nantialiasing=1\ngenerate_mipmaps=false\ndisable_embedded_bitmaps=true\nmultichannel_signed_distance_field=false\nmsdf_pixel_range=8\nmsdf_size=48\nallow_system_fallback=true\nforce_autohinter=false\nhinting=1\nsubpixel_positioning=1\noversampling=0.0\nFallbacks=null\nfallbacks=[]\nCompress=null\ncompress=true\npreload=[]\nlanguage_support={}\nscript_support={}\nopentype_features={}\n"
  },
  {
    "path": "addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-ThinItalic.ttf.import",
    "content": "[remap]\n\nimporter=\"font_data_dynamic\"\ntype=\"FontFile\"\nuid=\"uid://cvh21iixbcfww\"\npath=\"res://.godot/imported/RobotoMono-ThinItalic.ttf-e9ceff3e4cdfbfedd19dbb3d3bba724a.fontdata\"\n\n[deps]\n\nsource_file=\"res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-ThinItalic.ttf\"\ndest_files=[\"res://.godot/imported/RobotoMono-ThinItalic.ttf-e9ceff3e4cdfbfedd19dbb3d3bba724a.fontdata\"]\n\n[params]\n\nRendering=null\nantialiasing=1\ngenerate_mipmaps=false\ndisable_embedded_bitmaps=true\nmultichannel_signed_distance_field=false\nmsdf_pixel_range=8\nmsdf_size=48\nallow_system_fallback=true\nforce_autohinter=false\nhinting=1\nsubpixel_positioning=1\noversampling=0.0\nFallbacks=null\nfallbacks=[]\nCompress=null\ncompress=true\npreload=[]\nlanguage_support={}\nscript_support={}\nopentype_features={}\n"
  },
  {
    "path": "addons/gdUnit4/src/update/assets/horizontal-line2.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://dgaa5faajesgv\"\npath=\"res://.godot/imported/horizontal-line2.png-92618e6ee5cc9002847547a8c9deadbc.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/gdUnit4/src/update/assets/horizontal-line2.png\"\ndest_files=[\"res://.godot/imported/horizontal-line2.png-92618e6ee5cc9002847547a8c9deadbc.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/LICENSE.md",
    "content": "Copyright (c) 2024-present Jan Thomä\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "addons/guide/debugger/guide_debugger.gd",
    "content": "extends MarginContainer\n\n@onready var _actions:Container = %Actions\n@onready var _inputs:Container = %Inputs\n@onready var _priorities:Container = %Priorities\n@onready var _formatter:GUIDEInputFormatter = GUIDEInputFormatter.for_active_contexts()\n\n\nfunc _ready() -> void:\n\tprocess_mode = Node.PROCESS_MODE_ALWAYS\n\tGUIDE.input_mappings_changed.connect(_update_priorities)\n\t_update_priorities()\n\nfunc _process(delta) -> void:\n\tif not is_visible_in_tree():\n\t\treturn\n\t\t\n\tvar index:int = 0\n\tfor mapping in GUIDE._active_action_mappings:\n\t\tvar action:GUIDEAction = mapping.action\n\n\t\tvar action_name:String = action.name\n\t\tif action_name == \"\":\n\t\t\taction_name = action._editor_name()\n\t\t\t\n\t\tvar action_state:String = \"\"\n\t\tmatch(action._last_state):\n\t\t\tGUIDEAction.GUIDEActionState.COMPLETED:\n\t\t\t\taction_state = \"Completed\"\n\t\t\tGUIDEAction.GUIDEActionState.ONGOING:\n\t\t\t\taction_state = \"Ongoing\"\n\t\t\tGUIDEAction.GUIDEActionState.TRIGGERED:\n\t\t\t\taction_state = \"Triggered\"\n\t\t\t\t\n\t\tvar action_value:String = \"\"\n\t\tmatch(action.action_value_type):\n\t\t\tGUIDEAction.GUIDEActionValueType.BOOL:\n\t\t\t\taction_value = str(action.value_bool)\n\t\t\tGUIDEAction.GUIDEActionValueType.AXIS_1D:\n\t\t\t\taction_value = str(action.value_axis_1d)\n\t\t\tGUIDEAction.GUIDEActionValueType.AXIS_2D:\n\t\t\t\taction_value = str(action.value_axis_2d)\n\t\t\tGUIDEAction.GUIDEActionValueType.AXIS_3D:\n\t\t\t\taction_value = str(action.value_axis_3d)\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\n\t\tvar label := _get_label(_actions, index)\n\t\tlabel.text = \"[%s] %s - %s\" % [action_name, action_state, action_value]\n\t\t\n\t\tindex += 1\n\t\t\n\t# Clean out all labels we don't need anymore\n\t_cleanup(_actions, index)\n\t\n\tindex = 0\n\tfor input in GUIDE._active_inputs.values():\n\t\tvar input_label := _formatter.input_as_text(input, false)\t\n\t\tvar input_value:String = str(input._value)\n\n\t\tvar label := _get_label(_inputs, index)\n\t\tlabel.text = \"%s - %s\" % [input_label, input_value]\n\t\tindex += 1\n\t\t\n\t_cleanup(_inputs, index)\n\n\nfunc _get_label(container:Container, index:int) -> Label:\n\tvar label:Label = null\n\tif container.get_child_count() > index:\n\t\t# reuse existing label\n\t\tlabel = container.get_child(index)\n\telse:\n\t\t# make a new one\n\t\tlabel = Label.new()\n\t\tlabel.mouse_filter = Control.MOUSE_FILTER_IGNORE\n\t\tcontainer.add_child(label)\n\treturn label\n\t\nfunc _cleanup(container:Container, index:int) -> void:\n\twhile container.get_child_count() > index:\n\t\tvar to_free := container.get_child(index)\n\t\tcontainer.remove_child(to_free)\t\t\n\t\tto_free.queue_free()\t\n\nfunc _update_priorities() -> void:\n\t# since we don't update these per frame, we can just clear them out and \n\t# rebuild them when mapping contexts change\n\t_cleanup(_priorities, 0)\n\t\n\tfor mapping:GUIDEActionMapping in GUIDE._active_action_mappings:\n\t\tvar action := mapping.action\n\t\tif GUIDE._actions_sharing_input.has(action):\n\t\t\tvar label := Label.new()\n\t\t\tvar names := \", \".join(GUIDE._actions_sharing_input[action].map(func(it): return it._editor_name()))\n\t\t\tlabel.text = \"[%s] > [%s]\" % [action._editor_name(), names]\n\t\t\t_priorities.add_child(label)\n\t\t\t\n\t\t\t\n\tif _priorities.get_child_count() == 0:\n\t\tvar label := Label.new()\n\t\tlabel.text = \"<no overlapping input>\"\n\t\t_priorities.add_child(label)\n"
  },
  {
    "path": "addons/guide/debugger/guide_debugger.gd.uid",
    "content": "uid://cqfnsis3hhdrv\n"
  },
  {
    "path": "addons/guide/debugger/guide_debugger.tscn",
    "content": "[gd_scene load_steps=2 format=3 uid=\"uid://dkr80d2pi0d41\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/debugger/guide_debugger.gd\" id=\"1_ckdvj\"]\n\n[node name=\"GuideDebugger\" type=\"MarginContainer\"]\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\nmouse_filter = 2\nscript = ExtResource(\"1_ckdvj\")\n\n[node name=\"VBoxContainer\" type=\"VBoxContainer\" parent=\".\"]\nlayout_mode = 2\nmouse_filter = 2\n\n[node name=\"Label\" type=\"Label\" parent=\"VBoxContainer\"]\nlayout_mode = 2\ntext = \"G.U.I.D.E - Debugger\"\n\n[node name=\"Label2\" type=\"Label\" parent=\"VBoxContainer\"]\nlayout_mode = 2\ntext = \"Actions\"\n\n[node name=\"Actions\" type=\"VFlowContainer\" parent=\"VBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_vertical = 3\nmouse_filter = 2\n\n[node name=\"Label3\" type=\"Label\" parent=\"VBoxContainer\"]\nlayout_mode = 2\ntext = \"Inputs\"\n\n[node name=\"Inputs\" type=\"VFlowContainer\" parent=\"VBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_vertical = 3\nmouse_filter = 2\n\n[node name=\"Label4\" type=\"Label\" parent=\"VBoxContainer\"]\nlayout_mode = 2\ntext = \"Action Priority\"\n\n[node name=\"Priorities\" type=\"VFlowContainer\" parent=\"VBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_vertical = 3\nmouse_filter = 2\n"
  },
  {
    "path": "addons/guide/editor/action_mapping_editor/action_mapping_editor.gd",
    "content": "@tool\nextends MarginContainer\n\nconst ActionSlot = preload(\"../action_slot/action_slot.gd\")\nconst Utils = preload(\"../utils.gd\")\nconst ArrayEdit = preload(\"../array_edit/array_edit.gd\")\n\nsignal delete_requested()\nsignal duplicate_requested()\n\n@export var input_mapping_editor_scene:PackedScene\n@onready var _action_slot:ActionSlot = %ActionSlot\n@onready var _input_mappings:ArrayEdit = %InputMappings\n\nconst ClassScanner = preload(\"../class_scanner.gd\")\n\nvar _plugin:EditorPlugin\nvar _undo_redo:EditorUndoRedoManager\n\nvar _mapping:GUIDEActionMapping\n\nfunc _ready():\n\t_action_slot.action_changed.connect(_on_action_changed)\n\t_input_mappings.show_insert_options = true\n\t_input_mappings.delete_requested.connect(_on_input_mapping_delete_requested)\n\t_input_mappings.add_requested.connect(_on_input_mappings_add_requested)\n\t_input_mappings.move_requested.connect(_on_input_mappings_move_requested)\n\t_input_mappings.clear_requested.connect(_on_input_mappings_clear_requested)\n\t_input_mappings.duplicate_requested.connect(_on_input_mappings_duplicate_requested)\n\t_input_mappings.insert_requested.connect(_on_input_mappings_insert_requested)\n\t_input_mappings.collapse_state_changed.connect(_on_input_mappings_collapse_state_changed)\n\nfunc initialize(plugin:EditorPlugin) -> void:\n\t_plugin = plugin\n\t_undo_redo = _plugin.get_undo_redo()\n\n\nfunc edit(mapping:GUIDEActionMapping) -> void:\n\tassert(_mapping == null)\n\t_mapping = mapping\n\n\t_mapping.changed.connect(_update)\n\n\t_update()\n\n\nfunc _update() -> void:\n\t_input_mappings.clear()\n\n\t_action_slot.action = _mapping.action\n\n\tfor i in _mapping.input_mappings.size():\n\t\tvar input_mapping := _mapping.input_mappings[i]\n\t\tvar input_mapping_editor := input_mapping_editor_scene.instantiate()\n\t\t_input_mappings.add_item(input_mapping_editor)\n\n\t\tinput_mapping_editor.initialize(_plugin)\n\t\tinput_mapping_editor.edit(input_mapping)\n\n\t_input_mappings.collapsed = _mapping.get_meta(\"_guide_input_mappings_collapsed\", false)\n\n\nfunc _on_action_changed():\n\t_undo_redo.create_action(\"Change action\")\n\t_undo_redo.add_do_property(_mapping, \"action\", _action_slot.action)\n\t_undo_redo.add_undo_property(_mapping, \"action\", _mapping.action)\n\t_undo_redo.commit_action()\n\n\nfunc _on_input_mappings_add_requested() -> void:\n\tvar values := _mapping.input_mappings.duplicate()\n\tvar new_mapping := GUIDEInputMapping.new()\n\tvalues.append(new_mapping)\n\n\t_undo_redo.create_action(\"Add input mapping\")\n\n\t_undo_redo.add_do_property(_mapping, \"input_mappings\", values)\n\t_undo_redo.add_undo_property(_mapping, \"input_mappings\", _mapping.input_mappings)\n\n\t_undo_redo.commit_action()\n\n\nfunc _on_input_mapping_delete_requested(index:int) -> void:\n\tvar values := _mapping.input_mappings.duplicate()\n\tvalues.remove_at(index)\n\n\t_undo_redo.create_action(\"Delete input mapping\")\n\t_undo_redo.add_do_property(_mapping, \"input_mappings\", values)\n\t_undo_redo.add_undo_property(_mapping, \"input_mappings\", _mapping.input_mappings)\n\n\t_undo_redo.commit_action()\n\n\nfunc _on_input_mappings_move_requested(from:int, to:int) -> void:\n\tvar values := _mapping.input_mappings.duplicate()\n\tvar mapping = values[from]\n\tvalues.remove_at(from)\n\tif from < to:\n\t\tto -= 1\n\tvalues.insert(to, mapping)\n\n\t_undo_redo.create_action(\"Move input mapping\")\n\t_undo_redo.add_do_property(_mapping, \"input_mappings\", values)\n\t_undo_redo.add_undo_property(_mapping, \"input_mappings\", _mapping.input_mappings)\n\n\t_undo_redo.commit_action()\n\n\nfunc _on_input_mappings_clear_requested() -> void:\n\tvar values:Array[GUIDEInputMapping] = []\n\t_undo_redo.create_action(\"Clear input mappings\")\n\t_undo_redo.add_do_property(_mapping, \"input_mappings\", values)\n\t_undo_redo.add_undo_property(_mapping, \"input_mappings\", _mapping.input_mappings)\n\n\t_undo_redo.commit_action()\n\nfunc _on_input_mappings_duplicate_requested(index:int) -> void:\n\tvar values := _mapping.input_mappings.duplicate()\n\tvar copy:GUIDEInputMapping = values[index].duplicate()\n\tcopy.input = Utils.duplicate_if_inline(copy.input)\n\n\tfor i in copy.modifiers.size():\n\t\tcopy.modifiers[i] = Utils.duplicate_if_inline(copy.modifiers[i])\n\n\tfor i in copy.triggers.size():\n\t\tcopy.triggers[i] = Utils.duplicate_if_inline(copy.triggers[i])\n\n\t# insert copy after original\n\tvalues.insert(index+1, copy)\n\n\t_undo_redo.create_action(\"Duplicate input mapping\")\n\t_undo_redo.add_do_property(_mapping, \"input_mappings\", values)\n\t_undo_redo.add_undo_property(_mapping, \"input_mappings\", _mapping.input_mappings)\n\n\t_undo_redo.commit_action()\n\nfunc _on_input_mappings_insert_requested(index:int) -> void:\n\t\n\tvar values := _mapping.input_mappings.duplicate()\n\tvar mapping:GUIDEInputMapping = GUIDEInputMapping.new()\n\n\t# insert copy at the index\n\tvalues.insert(index, mapping)\n\n\t_undo_redo.create_action(\"Insert input mapping\")\n\t_undo_redo.add_do_property(_mapping, \"input_mappings\", values)\n\t_undo_redo.add_undo_property(_mapping, \"input_mappings\", _mapping.input_mappings)\n\n\t_undo_redo.commit_action()\n\nfunc _on_input_mappings_collapse_state_changed(new_state:bool):\n\t_mapping.set_meta(\"_guide_input_mappings_collapsed\", new_state)\n\n"
  },
  {
    "path": "addons/guide/editor/action_mapping_editor/action_mapping_editor.gd.uid",
    "content": "uid://dp8xv83uhxpjo\n"
  },
  {
    "path": "addons/guide/editor/action_mapping_editor/action_mapping_editor.tscn",
    "content": "[gd_scene load_steps=5 format=3 uid=\"uid://361aipcef24h\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/editor/action_mapping_editor/action_mapping_editor.gd\" id=\"1_2k0pi\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://du4x7ng6ntuk4\" path=\"res://addons/guide/editor/action_slot/action_slot.tscn\" id=\"1_hguf2\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://c323mdijdhktg\" path=\"res://addons/guide/editor/input_mapping_editor/input_mapping_editor.tscn\" id=\"2_a8nbp\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://cly0ff32fvpb2\" path=\"res://addons/guide/editor/array_edit/array_edit.tscn\" id=\"4_ehr5j\"]\n\n[node name=\"ActionMappingEditor\" type=\"MarginContainer\"]\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\nsize_flags_vertical = 0\ntheme_override_constants/margin_bottom = 5\nscript = ExtResource(\"1_2k0pi\")\ninput_mapping_editor_scene = ExtResource(\"2_a8nbp\")\n\n[node name=\"HBoxContainer\" type=\"HBoxContainer\" parent=\".\"]\nlayout_mode = 2\nsize_flags_vertical = 0\n\n[node name=\"HBoxContainer\" type=\"HBoxContainer\" parent=\"HBoxContainer\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 0\n\n[node name=\"ActionSlot\" parent=\"HBoxContainer/HBoxContainer\" instance=ExtResource(\"1_hguf2\")]\nunique_name_in_owner = true\nlayout_mode = 2\n\n[node name=\"VBoxContainer\" type=\"VBoxContainer\" parent=\"HBoxContainer\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 0\nsize_flags_stretch_ratio = 4.0\n\n[node name=\"InputMappings\" parent=\"HBoxContainer/VBoxContainer\" instance=ExtResource(\"4_ehr5j\")]\nunique_name_in_owner = true\nlayout_mode = 2\ntitle = \"Input mappings\"\nadd_tooltip = \"Add input mapping\"\nclear_tooltip = \"Clear input mappings\"\n"
  },
  {
    "path": "addons/guide/editor/action_slot/action_slot.gd",
    "content": "@tool\nextends Control\n\nsignal action_changed()\n\n@onready var _line_edit:LineEdit = %LineEdit\n@onready var _type_icon:TextureRect = %TypeIcon\n\nvar index:int\n\nvar action:GUIDEAction:\n\tset(value):\n\t\tif is_instance_valid(action):\n\t\t\taction.changed.disconnect(_refresh)\n\t\t\n\t\taction = value\n\t\n\t\tif is_instance_valid(action):\n\t\t\taction.changed.connect(_refresh)\n\t\n\t\t# action_changed can only be emitted by \n\t\t# dragging an action into this, not when setting\n\t\t# the property\n\t\t_refresh()\n\n\t\t\nfunc _refresh():\n\tif not is_instance_valid(action):\n\t\t_line_edit.text = \"<none>\"\n\t\t_line_edit.tooltip_text = \"\"\n\t\t_type_icon.texture = preload(\"missing_action.svg\")\n\t\t_type_icon.tooltip_text = \"Missing action\"\n\telse:\n\t\t_line_edit.text = action._editor_name()\t\n\t\t_line_edit.tooltip_text = action.resource_path\n\t\t## Update the icon to reflect the given value type.\n\t\tmatch action.action_value_type:\n\t\t\tGUIDEAction.GUIDEActionValueType.AXIS_1D:\n\t\t\t\t_type_icon.texture = preload(\"action_value_type_axis1d.svg\")\n\t\t\t\t_type_icon.tooltip_text = \"Axis1D\"\n\t\t\tGUIDEAction.GUIDEActionValueType.AXIS_2D:\n\t\t\t\t_type_icon.texture = preload(\"action_value_type_axis2d.svg\")\n\t\t\t\t_type_icon.tooltip_text = \"Axis2D\"\n\t\t\tGUIDEAction.GUIDEActionValueType.AXIS_3D:\n\t\t\t\t_type_icon.texture = preload(\"action_value_type_axis3d.svg\")\n\t\t\t\t_type_icon.tooltip_text = \"Axis3D\"\n\t\t\t_:\n\t\t\t\t# fallback is bool\n\t\t\t\t_type_icon.texture = preload(\"action_value_type_bool.svg\")\n\t\t\t\t_type_icon.tooltip_text = \"Boolean\"\n\n\n\n\nfunc _gui_input(event):\n\tif event is InputEventMouseButton:\n\t\tif event.pressed and event.button_index == MOUSE_BUTTON_LEFT:\n\t\t\tif is_instance_valid(action):\n\t\t\t\tEditorInterface.edit_resource(action)\n\n\n\nfunc _on_line_edit_action_dropped(new_action:GUIDEAction):\n\taction = new_action\n\taction_changed.emit()\n\n\nfunc _on_line_edit_focus_entered():\n\tif is_instance_valid(action):\n\t\tEditorInterface.edit_resource(action)\n"
  },
  {
    "path": "addons/guide/editor/action_slot/action_slot.gd.uid",
    "content": "uid://ysrbdsqui5cn\n"
  },
  {
    "path": "addons/guide/editor/action_slot/action_slot.tscn",
    "content": "[gd_scene load_steps=3 format=3 uid=\"uid://du4x7ng6ntuk4\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/editor/action_slot/action_slot.gd\" id=\"1_w5nxd\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/editor/action_slot/action_slot_line_edit.gd\" id=\"2_ram7b\"]\n\n[node name=\"ActionSlot\" type=\"HBoxContainer\"]\noffset_right = 40.0\noffset_bottom = 40.0\nsize_flags_horizontal = 3\nscript = ExtResource(\"1_w5nxd\")\n\n[node name=\"TypeIcon\" type=\"TextureRect\" parent=\".\"]\nunique_name_in_owner = true\nlayout_mode = 2\nexpand_mode = 3\nstretch_mode = 4\n\n[node name=\"LineEdit\" type=\"LineEdit\" parent=\".\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 0\ntext = \"Name\"\neditable = false\nselecting_enabled = false\nscript = ExtResource(\"2_ram7b\")\n\n[connection signal=\"action_dropped\" from=\"LineEdit\" to=\".\" method=\"_on_line_edit_action_dropped\"]\n[connection signal=\"focus_entered\" from=\"LineEdit\" to=\".\" method=\"_on_line_edit_focus_entered\"]\n"
  },
  {
    "path": "addons/guide/editor/action_slot/action_slot_line_edit.gd",
    "content": "@tool\nextends LineEdit\n\nsignal action_dropped(action:GUIDEAction)\n\n\nfunc _can_drop_data(at_position, data) -> bool:\n\tif not data is Dictionary:\n\t\treturn false\n\t\t\n\tif data.has(\"files\"):\n\t\tfor file in data[\"files\"]:\n\t\t\tif ResourceLoader.load(file) is GUIDEAction:\n\t\t\t\treturn true\n\t\t\n\treturn false\t\n\t\n\t\nfunc _drop_data(at_position, data) -> void:\n\tfor file in data[\"files\"]:\n\t\tvar item := ResourceLoader.load(file) \n\t\tif item is GUIDEAction:\n\t\t\taction_dropped.emit(item)\n\n"
  },
  {
    "path": "addons/guide/editor/action_slot/action_slot_line_edit.gd.uid",
    "content": "uid://b12uq0dpsgj7u\n"
  },
  {
    "path": "addons/guide/editor/action_slot/action_value_type_axis1d.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://du55fdegui0t0\"\npath=\"res://.godot/imported/action_value_type_axis1d.svg-47cde6e873b547282e811542e4ee320d.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/editor/action_slot/action_value_type_axis1d.svg\"\ndest_files=[\"res://.godot/imported/action_value_type_axis1d.svg-47cde6e873b547282e811542e4ee320d.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/editor/action_slot/action_value_type_axis2d.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bw3r81rgkbeic\"\npath=\"res://.godot/imported/action_value_type_axis2d.svg-82a12ec01234cc4464e5fb9b94ba28f0.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/editor/action_slot/action_value_type_axis2d.svg\"\ndest_files=[\"res://.godot/imported/action_value_type_axis2d.svg-82a12ec01234cc4464e5fb9b94ba28f0.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/editor/action_slot/action_value_type_axis3d.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://dcsfko8g6vjor\"\npath=\"res://.godot/imported/action_value_type_axis3d.svg-6c96e9bad6748ae9f491c37a99292ee2.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/editor/action_slot/action_value_type_axis3d.svg\"\ndest_files=[\"res://.godot/imported/action_value_type_axis3d.svg-6c96e9bad6748ae9f491c37a99292ee2.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/editor/action_slot/action_value_type_bool.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bla3yu6pdqyt5\"\npath=\"res://.godot/imported/action_value_type_bool.svg-552c954344c23690bcca901351d04f59.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/editor/action_slot/action_value_type_bool.svg\"\ndest_files=[\"res://.godot/imported/action_value_type_bool.svg-552c954344c23690bcca901351d04f59.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/editor/action_slot/missing_action.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cdi5eoc1e8ha0\"\npath=\"res://.godot/imported/missing_action.svg-31774fd8d1b787aab90de376faa436ea.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/editor/action_slot/missing_action.svg\"\ndest_files=[\"res://.godot/imported/missing_action.svg-31774fd8d1b787aab90de376faa436ea.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=false\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/editor/array_edit/array_edit.gd",
    "content": "@tool\nextends Container\nconst Utils = preload(\"../utils.gd\")\n\n@export var item_scene:PackedScene\n\n@export var title:String = \"\":\n\tset(value):\n\t\ttitle = value\n\t\t_refresh()\n\n@export var add_tooltip:String:\n\tset(value):\n\t\tadd_tooltip = value\n\t\t_refresh()\n\n@export var clear_tooltip:String:\n\tset(value):\n\t\tclear_tooltip = value\n\t\t_refresh()\n\n@export var item_separation:int = 8:\n\tset(value):\n\t\titem_separation = value\n\t\t_refresh()\n\n\n@export var collapsed:bool = false:\n\tset(value):\n\t\tcollapsed = value\n\t\t_refresh()\n\n\n@export var show_insert_options:bool = false:\n\tset(value):\n\t\tshow_insert_options = value\n\t\t_build_popup()\n\n\nsignal add_requested()\nsignal delete_requested(index:int)\nsignal move_requested(from:int, to:int)\nsignal insert_requested(index:int)\nsignal duplicate_requested(index:int)\nsignal clear_requested()\nsignal collapse_state_changed(collapsed:bool)\n\n@onready var _add_button:Button = %AddButton\n@onready var _clear_button:Button = %ClearButton\n@onready var _contents:Container = %Contents\n@onready var _title_label:Label = %TitleLabel\n@onready var _collapse_button:Button = %CollapseButton\n@onready var _expand_button:Button = %ExpandButton\n@onready var _count_label:Label = %CountLabel\n@onready var _popup_menu:PopupMenu = %PopupMenu\n\nconst _ID_DELETE:int = 2\nconst _ID_DUPLICATE:int = 3\nconst _ID_INSERT_BEFORE:int = 4\nconst _ID_INSERT_AFTER:int = 5\n\nvar _active_item_index:int = -1\n\nfunc _ready():\n\t_add_button.icon = get_theme_icon(\"Add\", \"EditorIcons\")\n\t_add_button.pressed.connect(func(): add_requested.emit())\n\n\t_clear_button.icon = get_theme_icon(\"Clear\", \"EditorIcons\")\n\t_clear_button.pressed.connect(func(): clear_requested.emit())\n\n\t_collapse_button.icon = get_theme_icon(\"Collapse\", \"EditorIcons\")\n\t_collapse_button.pressed.connect(_on_collapse_pressed)\n\n\t_expand_button.icon = get_theme_icon(\"Forward\", \"EditorIcons\")\n\t_expand_button.pressed.connect(_on_expand_pressed)\n\n\t_popup_menu.id_pressed.connect(_on_popup_id_pressed)\n\t_build_popup()\n\t_refresh()\n\n\nfunc _build_popup() -> void:\n\tif not is_instance_valid(_popup_menu):\n\t\treturn\n\t_popup_menu.clear()\n\t_popup_menu.add_icon_item(get_theme_icon(\"Duplicate\", \"EditorIcons\"), \"Duplicate\", _ID_DUPLICATE)\n\tif show_insert_options:\n\t\t_popup_menu.add_icon_item(get_theme_icon(\"InsertBefore\", \"EditorIcons\"), \"Insert Before\", _ID_INSERT_BEFORE)\n\t\t_popup_menu.add_icon_item(get_theme_icon(\"InsertAfter\", \"EditorIcons\"), \"Insert After\", _ID_INSERT_AFTER)\n\t_popup_menu.add_icon_item(get_theme_icon(\"Remove\", \"EditorIcons\"), \"Delete\", _ID_DELETE)\n\n\nfunc _on_popup_id_pressed(id:int) -> void:\n\tmatch id:\n\t\t_ID_DELETE:\n\t\t\tdelete_requested.emit(_active_item_index)\n\t\t_ID_DUPLICATE:\n\t\t\tduplicate_requested.emit(_active_item_index)\n\t\t_ID_INSERT_BEFORE:\n\t\t\tinsert_requested.emit(_active_item_index)\n\t\t_ID_INSERT_AFTER:\n\t\t\tinsert_requested.emit(_active_item_index + 1)\n\n\nfunc _on_item_context_menu_requested(index:int, screen_position:Vector2) -> void:\n\t_active_item_index = index\n\t_popup_menu.popup(Rect2(screen_position, Vector2.ZERO))\n\n\nfunc _refresh():\n\tif is_instance_valid(_add_button):\n\t\t_add_button.tooltip_text = add_tooltip\n\tif is_instance_valid(_clear_button):\n\t\t_clear_button.tooltip_text = clear_tooltip\n\t\t_clear_button.visible = _contents.get_child_count() > 0\n\n\tif is_instance_valid(_contents):\n\t\t_contents.add_theme_constant_override(\"separation\", item_separation)\n\t\t_contents.visible = not collapsed\n\n\tif is_instance_valid(_collapse_button):\n\t\t_collapse_button.visible = not collapsed\n\n\tif is_instance_valid(_expand_button):\n\t\t_expand_button.visible = collapsed\n\n\tif is_instance_valid(_title_label):\n\t\t_title_label.text = title\n\n\tif is_instance_valid(_count_label):\n\t\t_count_label.text = \"(%s)\" % [_contents.get_child_count()]\n\n\nfunc clear():\n\tUtils.clear(_contents)\n\t_refresh()\n\n\nfunc add_item(new_item:Control):\n\tvar item_wrapper := item_scene.instantiate()\n\t_contents.add_child(item_wrapper)\n\titem_wrapper.initialize(new_item)\n\titem_wrapper.move_requested.connect(func(from:int, to:int): move_requested.emit(from, to))\n\titem_wrapper.context_menu_requested.connect(_on_item_context_menu_requested)\n\t_refresh()\n\n\nfunc _on_collapse_pressed():\n\tcollapsed = true\n\tcollapse_state_changed.emit(true)\n\n\nfunc _on_expand_pressed():\n\tcollapsed = false\n\tcollapse_state_changed.emit(false)"
  },
  {
    "path": "addons/guide/editor/array_edit/array_edit.gd.uid",
    "content": "uid://whm2ksw6nc4h\n"
  },
  {
    "path": "addons/guide/editor/array_edit/array_edit.tscn",
    "content": "[gd_scene load_steps=5 format=3 uid=\"uid://cly0ff32fvpb2\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/editor/array_edit/array_edit.gd\" id=\"1_y3qyt\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://cjabwsa4gmlpp\" path=\"res://addons/guide/editor/array_edit/array_edit_item.tscn\" id=\"2_n3ncl\"]\n\n[sub_resource type=\"Image\" id=\"Image_efj5n\"]\ndata = {\n\"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, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_uapko\"]\nimage = SubResource(\"Image_efj5n\")\n\n[node name=\"Array\" type=\"MarginContainer\"]\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\nscript = ExtResource(\"1_y3qyt\")\nitem_scene = ExtResource(\"2_n3ncl\")\nitem_separation = 10\n\n[node name=\"VBoxContainer\" type=\"VBoxContainer\" parent=\".\"]\nlayout_mode = 2\n\n[node name=\"MarginContainer\" type=\"MarginContainer\" parent=\"VBoxContainer\"]\nlayout_mode = 2\n\n[node name=\"Panel\" type=\"Panel\" parent=\"VBoxContainer/MarginContainer\"]\nlayout_mode = 2\n\n[node name=\"HBoxContainer\" type=\"HBoxContainer\" parent=\"VBoxContainer/MarginContainer\"]\nlayout_mode = 2\n\n[node name=\"CollapseButton\" type=\"Button\" parent=\"VBoxContainer/MarginContainer/HBoxContainer\"]\nunique_name_in_owner = true\ncustom_minimum_size = Vector2(32, 0)\nlayout_mode = 2\nsize_flags_horizontal = 0\ntooltip_text = \"Collapse\"\nicon = SubResource(\"ImageTexture_uapko\")\n\n[node name=\"ExpandButton\" type=\"Button\" parent=\"VBoxContainer/MarginContainer/HBoxContainer\"]\nunique_name_in_owner = true\nvisible = false\ncustom_minimum_size = Vector2(48, 0)\nlayout_mode = 2\nsize_flags_horizontal = 0\ntooltip_text = \"Expand\"\nicon = SubResource(\"ImageTexture_uapko\")\n\n[node name=\"AddButton\" type=\"Button\" parent=\"VBoxContainer/MarginContainer/HBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 0\nicon = SubResource(\"ImageTexture_uapko\")\n\n[node name=\"ClearButton\" type=\"Button\" parent=\"VBoxContainer/MarginContainer/HBoxContainer\"]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\nsize_flags_horizontal = 0\nicon = SubResource(\"ImageTexture_uapko\")\n\n[node name=\"MarginContainer\" type=\"MarginContainer\" parent=\"VBoxContainer/MarginContainer/HBoxContainer\"]\nlayout_mode = 2\n\n[node name=\"HBoxContainer\" type=\"HBoxContainer\" parent=\"VBoxContainer/MarginContainer/HBoxContainer/MarginContainer\"]\nlayout_mode = 2\n\n[node name=\"TitleLabel\" type=\"Label\" parent=\"VBoxContainer/MarginContainer/HBoxContainer/MarginContainer/HBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\n\n[node name=\"CountLabel\" type=\"Label\" parent=\"VBoxContainer/MarginContainer/HBoxContainer/MarginContainer/HBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntext = \"(0)\"\n\n[node name=\"Contents\" type=\"VBoxContainer\" parent=\"VBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntheme_override_constants/separation = 10\n\n[node name=\"PopupMenu\" type=\"PopupMenu\" parent=\".\"]\nunique_name_in_owner = true\n"
  },
  {
    "path": "addons/guide/editor/array_edit/array_edit_item.gd",
    "content": "@tool\nextends Container\nconst Utils = preload(\"../utils.gd\")\nconst Dragger = preload(\"dragger.gd\")\n\nsignal move_requested(from:int, to:int)\nsignal context_menu_requested(index:int, screen_position:Vector2)\n\n@onready var _dragger:Dragger = %Dragger\n@onready var _content:Container = %Content\n@onready var _before_indicator:ColorRect = %BeforeIndicator\n@onready var _after_indicator:ColorRect = %AfterIndicator\n\n\nfunc _ready():\n\t_dragger.icon = get_theme_icon(\"GuiSpinboxUpdown\", \"EditorIcons\")\n\t_before_indicator.color = get_theme_color(\"box_selection_stroke_color\", \"Editor\")\n\t_after_indicator.color = get_theme_color(\"box_selection_stroke_color\", \"Editor\")\n\t_before_indicator.visible = false\n\t_after_indicator.visible = false\n\t_dragger._parent_array = get_parent()\n\t_dragger._index = get_index()\n\t_dragger.pressed.connect(_on_dragger_pressed)\n\nfunc initialize(content:Control):\n\tUtils.clear(_content)\n\t_content.add_child(content)\n\n\nfunc _on_dragger_pressed() -> void:\n\tcontext_menu_requested.emit(get_index(), get_screen_position() + get_local_mouse_position())\n\n\nfunc _can_drop_data(at_position:Vector2, data) -> bool:\n\tif data is Dictionary and data.has(\"parent_array\") and data.parent_array == get_parent() and data.index != get_index():\n\t\tvar height := size.y\n\n\t\tvar is_before := not _is_last_child() or (at_position.y < height/2.0)\n\t\tif is_before and data.index == get_index() - 1:\n\t\t\t# don't allow the previous child to be inserted at its\n\t\t\t# own position\n\t\t\treturn false\n\n\t\t_before_indicator.visible = is_before\n\t\t_after_indicator.visible = not is_before\n\t\treturn true\n\n\treturn false\n\n\nfunc _drop_data(at_position:Vector2, data:Variant) -> void:\n\tvar height := size.y\n\tvar is_before := not _is_last_child() or (at_position.y < height/2.0)\n\tvar to := get_index() if is_before else get_index() + 1\n\tmove_requested.emit(data.index, to)\n\t_before_indicator.visible = false\n\t_after_indicator.visible = false\n\nfunc _is_last_child() -> bool:\n\treturn get_index() == get_parent().get_child_count() - 1\n\n\nfunc _on_mouse_exited() -> void:\n\t_before_indicator.visible = false\n\t_after_indicator.visible = false"
  },
  {
    "path": "addons/guide/editor/array_edit/array_edit_item.gd.uid",
    "content": "uid://dhqhut5enoj43\n"
  },
  {
    "path": "addons/guide/editor/array_edit/array_edit_item.tscn",
    "content": "[gd_scene load_steps=5 format=3 uid=\"uid://cjabwsa4gmlpp\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/editor/array_edit/array_edit_item.gd\" id=\"1_ujx05\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/editor/array_edit/dragger.gd\" id=\"2_53e2r\"]\n\n[sub_resource type=\"Image\" id=\"Image_efj5n\"]\ndata = {\n\"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, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_uapko\"]\nimage = SubResource(\"Image_efj5n\")\n\n[node name=\"ArrayEditItem\" type=\"MarginContainer\"]\nanchors_preset = 10\nanchor_right = 1.0\noffset_bottom = 8.0\ngrow_horizontal = 2\nscript = ExtResource(\"1_ujx05\")\n\n[node name=\"MarginContainer\" type=\"MarginContainer\" parent=\".\"]\nlayout_mode = 2\ntheme_override_constants/margin_top = 2\ntheme_override_constants/margin_bottom = 2\n\n[node name=\"HBoxContainer\" type=\"HBoxContainer\" parent=\"MarginContainer\"]\nlayout_mode = 2\n\n[node name=\"Dragger\" type=\"Button\" parent=\"MarginContainer/HBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 0\ntooltip_text = \"Drag to reorder, click for options.\"\nfocus_mode = 0\nmouse_filter = 1\nicon = SubResource(\"ImageTexture_uapko\")\nscript = ExtResource(\"2_53e2r\")\n\n[node name=\"Content\" type=\"MarginContainer\" parent=\"MarginContainer/HBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 3\n\n[node name=\"VBoxContainer\" type=\"VBoxContainer\" parent=\".\"]\nlayout_mode = 2\nmouse_filter = 2\n\n[node name=\"BeforeIndicator\" type=\"ColorRect\" parent=\"VBoxContainer\"]\nunique_name_in_owner = true\nvisible = false\ncustom_minimum_size = Vector2(0, 2)\nlayout_mode = 2\nmouse_filter = 2\ncolor = Color(0, 0, 0, 1)\n\n[node name=\"Control\" type=\"Control\" parent=\"VBoxContainer\"]\nlayout_mode = 2\nsize_flags_vertical = 3\nmouse_filter = 2\n\n[node name=\"AfterIndicator\" type=\"ColorRect\" parent=\"VBoxContainer\"]\nunique_name_in_owner = true\nvisible = false\ncustom_minimum_size = Vector2(0, 2)\nlayout_mode = 2\nmouse_filter = 2\ncolor = Color(0, 0, 0, 1)\n\n[connection signal=\"mouse_exited\" from=\".\" to=\".\" method=\"_on_mouse_exited\"]\n"
  },
  {
    "path": "addons/guide/editor/array_edit/dragger.gd",
    "content": "@tool\nextends Button\n\nvar _parent_array:Variant\nvar _index:int\n\nfunc _get_drag_data(at_position) -> Variant:\n\treturn { \"parent_array\" : _parent_array, \"index\" : _index }\n"
  },
  {
    "path": "addons/guide/editor/array_edit/dragger.gd.uid",
    "content": "uid://d3cob8fbf0xk8\n"
  },
  {
    "path": "addons/guide/editor/binding_dialog/binding_dialog.gd",
    "content": "@tool\nextends Window\n\nconst ClassScanner = preload(\"../class_scanner.gd\")\nconst Utils = preload(\"../utils.gd\")\n\nsignal input_selected(input:GUIDEInput)\n\n@onready var _input_display:RichTextLabel = %InputDisplay\n@onready var _available_types:Container = %AvailableTypes\n@onready var _none_available:Control = %NoneAvailable\n@onready var _some_available:Control = %SomeAvailable\n@onready var _select_bool_button:Button = %SelectBoolButton\n@onready var _select_1d_button:Button = %Select1DButton\n@onready var _select_2d_button:Button = %Select2DButton\n@onready var _select_3d_button:Button = %Select3DButton\n@onready var _instructions_label:Label = %InstructionsLabel\n@onready var _accept_detection_button:Button = %AcceptDetectionButton\n@onready var _input_detector:GUIDEInputDetector = %InputDetector\n@onready var _detect_bool_button:Button = %DetectBoolButton\n@onready var _detect_1d_button:Button = %Detect1DButton\n@onready var _detect_2d_button:Button = %Detect2DButton\n@onready var _detect_3d_button:Button = %Detect3DButton\n\nvar _last_detected_input:GUIDEInput\n\n\t\nfunc initialize() -> void:\n\t_setup_dialog()\n\t\nfunc _setup_dialog() -> void:\n\t# we need to bind this here. if we bind it in the editor, the editor\n\t# will crash when opening the scene because it will delete the node it\n\t# just tries to edit.\n\tfocus_exited.connect(_on_close_requested)\n\t\n\t_show_inputs_of_value_type(GUIDEAction.GUIDEActionValueType.BOOL)\n\t_instructions_label.text = tr(\"Press one of the buttons above to detect an input.\")\n\t_accept_detection_button.visible = false\n\t\n\nfunc _on_close_requested():\n\thide()\n\tqueue_free()\n\n\nfunc _show_inputs_of_value_type(type:GUIDEAction.GUIDEActionValueType) -> void:\n\tvar items:Array[GUIDEInput] = []\n\t\n\t_select_bool_button.set_pressed_no_signal(type == GUIDEAction.GUIDEActionValueType.BOOL)\n\t_select_1d_button.set_pressed_no_signal(type == GUIDEAction.GUIDEActionValueType.AXIS_1D)\n\t_select_2d_button.set_pressed_no_signal(type == GUIDEAction.GUIDEActionValueType.AXIS_2D)\n\t_select_3d_button.set_pressed_no_signal(type == GUIDEAction.GUIDEActionValueType.AXIS_3D)\n\t\n\tvar all_inputs := ClassScanner.find_inheritors(\"GUIDEInput\")\n\tfor script in all_inputs.values():\n\t\tvar dummy:GUIDEInput = script.new()\n\t\tif dummy._native_value_type() == type:\n\t\t\titems.append(dummy)\n\t\t\t\n\t_some_available.visible = not items.is_empty()\n\t_none_available.visible = items.is_empty()\n\t\n\tif items.is_empty():\n\t\treturn\n\t\t\n\titems.sort_custom(func(a,b): return a._editor_name().nocasecmp_to(b._editor_name()) < 0)\n\tUtils.clear(_available_types)\n\t\n\tfor item in items:\n\t\tvar button := Button.new()\n\t\tbutton.text = item._editor_name()\n\t\tbutton.tooltip_text = item._editor_description()\n\t\tbutton.pressed.connect(_deliver.bind(item))\t\n\t\tbutton.size_flags_horizontal = Control.SIZE_EXPAND_FILL\n\t\t\n\t\t_available_types.add_child(button)\t\n\t\t\n\t\nfunc _deliver(input:GUIDEInput):\n\tinput_selected.emit(input)\n\thide()\n\tqueue_free()\n\n\nfunc _on_select_bool_button_pressed():\n\t_show_inputs_of_value_type(GUIDEAction.GUIDEActionValueType.BOOL)\n\n\nfunc _on_select_1d_button_pressed():\n\t_show_inputs_of_value_type(GUIDEAction.GUIDEActionValueType.AXIS_1D)\n\n\nfunc _on_select_2d_button_pressed():\n\t_show_inputs_of_value_type(GUIDEAction.GUIDEActionValueType.AXIS_2D)\n\n\nfunc _on_select_3d_button_pressed():\n\t_show_inputs_of_value_type(GUIDEAction.GUIDEActionValueType.AXIS_3D)\n\n\nfunc _on_input_detector_detection_started():\n\t_instructions_label.text = tr(\"Actuate the input now...\")\n\n\nfunc _on_input_detector_input_detected(input:GUIDEInput):\n\t_instructions_label.visible = false\n\t_input_display.visible = true\n\t_input_display.input = input\n\t_accept_detection_button.visible = true\n\t_last_detected_input = input\n\n\nfunc _begin_detect_input(type:GUIDEAction.GUIDEActionValueType):\n\t_last_detected_input = null\n\t_instructions_label.visible = true\n\t_instructions_label.text = tr(\"Get ready...\")\n\t_accept_detection_button.visible = false\n\t_input_display.visible = false\n\t_input_detector.detect(type)\n\t\n\nfunc _on_detect_bool_button_pressed():\n\t_detect_bool_button.release_focus()\n\t_begin_detect_input(GUIDEAction.GUIDEActionValueType.BOOL)\n\n\nfunc _on_detect_1d_button_pressed():\n\t_detect_1d_button.release_focus()\n\t_begin_detect_input(GUIDEAction.GUIDEActionValueType.AXIS_1D)\n\n\nfunc _on_detect_2d_button_pressed():\n\t_detect_2d_button.release_focus()\n\t_begin_detect_input(GUIDEAction.GUIDEActionValueType.AXIS_2D)\n\n\nfunc _on_detect_3d_button_pressed():\n\t_detect_3d_button.release_focus()\n\t_begin_detect_input(GUIDEAction.GUIDEActionValueType.AXIS_3D)\n\t\n\nfunc _on_accept_detection_button_pressed():\n\tinput_selected.emit(_last_detected_input)\n\thide()\n\tqueue_free\n"
  },
  {
    "path": "addons/guide/editor/binding_dialog/binding_dialog.gd.uid",
    "content": "uid://dfuj0dl8ob6r6\n"
  },
  {
    "path": "addons/guide/editor/binding_dialog/binding_dialog.tscn",
    "content": "[gd_scene load_steps=5 format=3 uid=\"uid://dic27bm4pfw3q\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/editor/binding_dialog/binding_dialog.gd\" id=\"1_tknjd\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://dsv7s6tfmnsrs\" path=\"res://addons/guide/editor/input_display/input_display.tscn\" id=\"2_83ieu\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/remapping/guide_input_detector.gd\" id=\"3_c6q6r\"]\n\n[sub_resource type=\"StyleBoxFlat\" id=\"StyleBoxFlat_3e874\"]\ncontent_margin_left = 4.0\ncontent_margin_top = 4.0\ncontent_margin_right = 4.0\ncontent_margin_bottom = 4.0\nbg_color = Color(1, 0.365, 0.365, 1)\ndraw_center = false\nborder_width_left = 2\nborder_width_top = 2\nborder_width_right = 2\nborder_width_bottom = 2\ncorner_detail = 1\n\n[node name=\"BindingDialog\" type=\"Window\"]\ntitle = \"Input Configuration\"\ninitial_position = 4\nsize = Vector2i(1200, 600)\npopup_window = true\nmin_size = Vector2i(1200, 600)\nscript = ExtResource(\"1_tknjd\")\n\n[node name=\"MarginContainer\" type=\"MarginContainer\" parent=\".\"]\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\ntheme_override_constants/margin_bottom = 5\n\n[node name=\"BGPanel\" type=\"Panel\" parent=\"MarginContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntheme_override_styles/panel = SubResource(\"StyleBoxFlat_3e874\")\n\n[node name=\"MarginContainer\" type=\"MarginContainer\" parent=\"MarginContainer\"]\nlayout_mode = 2\ntheme_override_constants/margin_left = 5\ntheme_override_constants/margin_top = 5\ntheme_override_constants/margin_right = 5\ntheme_override_constants/margin_bottom = 5\n\n[node name=\"HBoxContainer\" type=\"HBoxContainer\" parent=\"MarginContainer/MarginContainer\"]\nlayout_mode = 2\n\n[node name=\"MarginContainer\" type=\"MarginContainer\" parent=\"MarginContainer/MarginContainer/HBoxContainer\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\n\n[node name=\"LeftPanel\" type=\"Panel\" parent=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\n\n[node name=\"MarginContainer\" type=\"MarginContainer\" parent=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer\"]\nlayout_mode = 2\ntheme_override_constants/margin_left = 5\ntheme_override_constants/margin_top = 5\ntheme_override_constants/margin_right = 5\ntheme_override_constants/margin_bottom = 5\n\n[node name=\"VBoxContainer\" type=\"VBoxContainer\" parent=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\ntheme_override_constants/separation = 10\n\n[node name=\"Label\" type=\"Label\" parent=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer\"]\nlayout_mode = 2\ntext = \"Detect Input\"\nhorizontal_alignment = 1\n\n[node name=\"HBoxContainer\" type=\"HBoxContainer\" parent=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer\"]\nlayout_mode = 2\n\n[node name=\"DetectBoolButton\" type=\"Button\" parent=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer/HBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 3\ntext = \"Boolean\"\n\n[node name=\"Detect1DButton\" type=\"Button\" parent=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer/HBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 3\ntext = \"1D\"\n\n[node name=\"Detect2DButton\" type=\"Button\" parent=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer/HBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 3\ntext = \"2D\"\n\n[node name=\"Detect3DButton\" type=\"Button\" parent=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer/HBoxContainer\"]\nunique_name_in_owner = true\ncustom_minimum_size = Vector2(80, 0)\nlayout_mode = 2\nsize_flags_horizontal = 3\ntext = \"3D\"\n\n[node name=\"InstructionsLabel\" type=\"Label\" parent=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_vertical = 6\ntext = \"3..2..1..\"\nhorizontal_alignment = 1\nvertical_alignment = 1\nautowrap_mode = 2\n\n[node name=\"InputDisplay\" parent=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer\" instance=ExtResource(\"2_83ieu\")]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_vertical = 6\n\n[node name=\"AcceptDetectionButton\" type=\"Button\" parent=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 4\ntext = \"Accept\"\n\n[node name=\"MarginContainer2\" type=\"MarginContainer\" parent=\"MarginContainer/MarginContainer/HBoxContainer\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\n\n[node name=\"RightPanel\" type=\"Panel\" parent=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer2\"]\nunique_name_in_owner = true\nlayout_mode = 2\n\n[node name=\"MarginContainer\" type=\"MarginContainer\" parent=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer2\"]\nlayout_mode = 2\ntheme_override_constants/margin_left = 5\ntheme_override_constants/margin_top = 5\ntheme_override_constants/margin_right = 5\ntheme_override_constants/margin_bottom = 5\n\n[node name=\"VBoxContainer\" type=\"VBoxContainer\" parent=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\ntheme_override_constants/separation = 10\n\n[node name=\"Label\" type=\"Label\" parent=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer/VBoxContainer\"]\nlayout_mode = 2\ntext = \"Select Input\"\nhorizontal_alignment = 1\n\n[node name=\"HBoxContainer\" type=\"HBoxContainer\" parent=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer/VBoxContainer\"]\nlayout_mode = 2\n\n[node name=\"SelectBoolButton\" type=\"Button\" parent=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer/VBoxContainer/HBoxContainer\"]\nunique_name_in_owner = true\ncustom_minimum_size = Vector2(80, 0)\nlayout_mode = 2\nsize_flags_horizontal = 3\ntoggle_mode = true\ntext = \"Boolean\"\n\n[node name=\"Select1DButton\" type=\"Button\" parent=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer/VBoxContainer/HBoxContainer\"]\nunique_name_in_owner = true\ncustom_minimum_size = Vector2(80, 0)\nlayout_mode = 2\nsize_flags_horizontal = 3\ntoggle_mode = true\ntext = \"1D\"\n\n[node name=\"Select2DButton\" type=\"Button\" parent=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer/VBoxContainer/HBoxContainer\"]\nunique_name_in_owner = true\ncustom_minimum_size = Vector2(80, 0)\nlayout_mode = 2\nsize_flags_horizontal = 3\ntoggle_mode = true\ntext = \"2D\"\n\n[node name=\"Select3DButton\" type=\"Button\" parent=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer/VBoxContainer/HBoxContainer\"]\nunique_name_in_owner = true\ncustom_minimum_size = Vector2(80, 0)\nlayout_mode = 2\nsize_flags_horizontal = 3\ntoggle_mode = true\ntext = \"3D\"\n\n[node name=\"NoneAvailable\" type=\"Label\" parent=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer/VBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 6\nsize_flags_vertical = 6\ntext = \"No matching inputs available.\"\n\n[node name=\"SomeAvailable\" type=\"ScrollContainer\" parent=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer/VBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_vertical = 3\n\n[node name=\"AvailableTypes\" type=\"VBoxContainer\" parent=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer/VBoxContainer/SomeAvailable\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 3\n\n[node name=\"InputDetector\" type=\"Node\" parent=\".\"]\nunique_name_in_owner = true\nscript = ExtResource(\"3_c6q6r\")\n\n[connection signal=\"close_requested\" from=\".\" to=\".\" method=\"_on_close_requested\"]\n[connection signal=\"pressed\" from=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer/HBoxContainer/DetectBoolButton\" to=\".\" method=\"_on_detect_bool_button_pressed\"]\n[connection signal=\"pressed\" from=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer/HBoxContainer/Detect1DButton\" to=\".\" method=\"_on_detect_1d_button_pressed\"]\n[connection signal=\"pressed\" from=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer/HBoxContainer/Detect2DButton\" to=\".\" method=\"_on_detect_2d_button_pressed\"]\n[connection signal=\"pressed\" from=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer/HBoxContainer/Detect3DButton\" to=\".\" method=\"_on_detect_3d_button_pressed\"]\n[connection signal=\"pressed\" from=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer/AcceptDetectionButton\" to=\".\" method=\"_on_accept_detection_button_pressed\"]\n[connection signal=\"pressed\" from=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer/VBoxContainer/HBoxContainer/SelectBoolButton\" to=\".\" method=\"_on_select_bool_button_pressed\"]\n[connection signal=\"pressed\" from=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer/VBoxContainer/HBoxContainer/Select1DButton\" to=\".\" method=\"_on_select_1d_button_pressed\"]\n[connection signal=\"pressed\" from=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer/VBoxContainer/HBoxContainer/Select2DButton\" to=\".\" method=\"_on_select_2d_button_pressed\"]\n[connection signal=\"pressed\" from=\"MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer/VBoxContainer/HBoxContainer/Select3DButton\" to=\".\" method=\"_on_select_3d_button_pressed\"]\n[connection signal=\"detection_started\" from=\"InputDetector\" to=\".\" method=\"_on_input_detector_detection_started\"]\n[connection signal=\"input_detected\" from=\"InputDetector\" to=\".\" method=\"_on_input_detector_input_detected\"]\n"
  },
  {
    "path": "addons/guide/editor/class_scanner.gd",
    "content": "## Scanner to find inheriting classes. Used to detect inheritors of\n## modifiers and triggers. Ideally this would be built into the editor\n## but sometimes one has to hack their way around the limitations.\n## This only scans to the extent needed to drive the UI, it's not a general\n## purpose implementation.\n@tool\n\nconst GUIDESet = preload(\"../guide_set.gd\")\nconst ClassScanner = preload(\"class_scanner.gd\")\n\nstatic var _dirty:bool = true\n\n# looks like we only get very limited access to the script's inheritance tree,\n# so we need to do a little caching ourselves\nstatic var _script_lut:Dictionary = {}\n\nstatic func _static_init():\n\tEditorInterface.get_resource_filesystem().script_classes_updated.connect(Callable(ClassScanner, \"_mark_dirty\"))\n\n\nstatic func _mark_dirty():\n\t_dirty = true\n\n## Returns all classes that directly or indirectly inherit from the \n## given class. Only works for scripts in the project, e.g. doesn't\n## scan the whole class_db. Key is class name, value is the Script instance\nstatic func find_inheritors(clazz_name:StringName) -> Dictionary:\n\tvar result:Dictionary = {}\n\n\tvar root := EditorInterface.get_resource_filesystem().get_filesystem()\n\t\n\t# rebuild the LUT when needed\n\tif _dirty:\n\t\t_script_lut.clear()\n\t\t_scan(root)\n\t\t_dirty = false\n\t\t\n\t\n\tvar open_set:GUIDESet = GUIDESet.new()\n\t# a closed set just to avoid infinite loops, we'll never\n\t# look at the same class more than once.\n\tvar closed_set:GUIDESet = GUIDESet.new()\n\t\n\topen_set.add(clazz_name)\n\t\n\twhile not open_set.is_empty():\n\t\tvar next = open_set.pull()\n\t\tclosed_set.add(next)\n\t\tif not _script_lut.has(next):\n\t\t\t# we don't know this script, ignore, move on\n\t\t\tcontinue\n\t\t\n\t\t# now find all scripts that extend the one we \n\t\t# are looking at\n\t\tfor item:ScriptInfo in _script_lut.values():\n\t\t\tif item.extendz == next:\n\t\t\t\t# put them into the result\n\t\t\t\tresult[item.clazz_name] = item.clazz_script\n\t\t\t\t# and put their class in the open set\n\t\t\t\t# unless we already looked at it.\n\t\t\t\tif not closed_set.has(item.clazz_name):\n\t\t\t\t\topen_set.add(item.clazz_name)\n\t\t\n\treturn result\n\n\nstatic func _scan(folder:EditorFileSystemDirectory) -> void:\n\tfor i in folder.get_file_count():\n\t\tvar script_clazz := folder.get_file_script_class_name(i)\n\t\tif script_clazz != \"\":\n\t\t\tvar info := _script_lut.get(script_clazz)\n\t\t\tif info == null:\n\t\t\t\tinfo = ScriptInfo.new()\n\t\t\t\tinfo.clazz_name = script_clazz\n\t\t\t\tinfo.clazz_script = ResourceLoader.load(folder.get_file_path(i))\n\t\t\t\t_script_lut[script_clazz] = info\n\t\t\t\t\n\t\t\tvar script_extendz := folder.get_file_script_class_extends(i)\n\t\t\tinfo.extendz = script_extendz\n\t\t\t\n\tfor i in folder.get_subdir_count():\n\t\t_scan(folder.get_subdir(i))\t\t\n\t\t\t\t\t\n\t\nclass ScriptInfo:\n\tvar clazz_name:StringName\n\tvar extendz:StringName\t\n\tvar clazz_script:Script\n\t\n\tfunc _to_string() -> String:\n\t\treturn clazz_name + \":\" + extendz\n\t\n"
  },
  {
    "path": "addons/guide/editor/class_scanner.gd.uid",
    "content": "uid://b1trdjs8ofe7c\n"
  },
  {
    "path": "addons/guide/editor/guide_project_settings.gd",
    "content": "@tool\n\nconst SETTING_EDITOR_JOY_ICONS = \"Guide/Editor/Joy Icons\"\nconst SETTING_EDITOR_JOY_TYPE = \"Guide/Editor/Joy Type\"\n\nstatic func initialize():\n\tif not ProjectSettings.has_setting(SETTING_EDITOR_JOY_ICONS):\n\t\teditor_joy_rendering = GUIDEInputFormattingOptions.JoyRendering.DEFAULT\n\t\n\tProjectSettings.set_initial_value(SETTING_EDITOR_JOY_ICONS, 0)\n\tProjectSettings.add_property_info({\n\t\t\"name\": SETTING_EDITOR_JOY_ICONS,\n\t\t\"type\": TYPE_INT,\n\t\t\"hint\": PROPERTY_HINT_ENUM,\n\t\t\"hint_string\": \"Detect from device:0,Use fixed joy type:2\",\n\t\t\"description\": \"Controls, how the joy icons are displayed in the mapping context editor.\"\n\t})\n\n\tif not ProjectSettings.has_setting(SETTING_EDITOR_JOY_TYPE):\n\t\teditor_joy_type = GUIDEInputFormattingOptions.JoyType.GENERIC_JOY\n\n\tProjectSettings.add_property_info({\n\t\t\"name\": SETTING_EDITOR_JOY_TYPE,\n\t\t\"type\": TYPE_INT,\n\t\t\"hint\": PROPERTY_HINT_ENUM,\n\t\t\"hint_string\": \"Generic Joy:0,Microsoft Controller:1,Nintendo Controller:2,Sony Controller:3,Steam Deck Controller:4\",\n\t\t\"description\": \"When a fixed joy type is used for rendering in the editor, this selects the icon set that is used.\"\n\t})\n\tProjectSettings.set_initial_value(SETTING_EDITOR_JOY_TYPE, 0)\n\t\n\t\n\t\nstatic var editor_joy_rendering:GUIDEInputFormattingOptions.JoyRendering:\n\tset(value):\n\t\tProjectSettings.set_setting(SETTING_EDITOR_JOY_ICONS, value)\n\tget:\n\t\treturn ProjectSettings.get_setting(SETTING_EDITOR_JOY_ICONS, GUIDEInputFormattingOptions.JoyRendering.DEFAULT)\n\t\t\nstatic var editor_joy_type:GUIDEInputFormattingOptions.JoyType:\n\tset(value):\n\t\tProjectSettings.set_setting(SETTING_EDITOR_JOY_TYPE, value)\n\tget:\n\t\treturn ProjectSettings.get_setting(SETTING_EDITOR_JOY_TYPE, GUIDEInputFormattingOptions.JoyType.GENERIC_JOY)\n"
  },
  {
    "path": "addons/guide/editor/guide_project_settings.gd.uid",
    "content": "uid://fdiwe5b4sinw2\n"
  },
  {
    "path": "addons/guide/editor/input_display/input_display.gd",
    "content": "@tool\nextends RichTextLabel\nsignal clicked()\n\nconst GUIDEProjectSettings = preload(\"../guide_project_settings.gd\")\n\nvar _formatter:GUIDEInputFormatter = GUIDEInputFormatter.new(64)\n\nfunc _ready() -> void:\n\tProjectSettings.settings_changed.connect(_refresh)\n\t\n\nvar input:GUIDEInput:\n\tset(value):\n\t\tif value == input:\n\t\t\treturn\n\t\t\n\t\tif is_instance_valid(input):\n\t\t\tinput.changed.disconnect(_refresh)\n\t\t\n\t\tinput = value\n\t\t\n\t\tif is_instance_valid(input):\n\t\t\tinput.changed.connect(_refresh)\n\n\t\t_refresh()\n\nfunc _refresh():\n\t_formatter.formatting_options.joy_rendering = GUIDEProjectSettings.editor_joy_rendering\n\t_formatter.formatting_options.preferred_joy_type = GUIDEProjectSettings.editor_joy_type\n\t\n\tif not is_instance_valid(input):\n\t\tparse_bbcode(\"[center][i]<not bound>[/i][/center]\")\n\t\ttooltip_text = \"\"\n\t\treturn\n\t\t\n\tvar text := await _formatter.input_as_richtext_async(input, false)\n\tparse_bbcode(\"[center]\" + text + \"[/center]\")\n\ttooltip_text = _formatter.input_as_text(input)\n\n \nfunc _gui_input(event):\n\tif event is InputEventMouseButton:\n\t\tif event.pressed and event.button_index == MOUSE_BUTTON_LEFT:\n\t\t\tclicked.emit()\n\n\n\t\n"
  },
  {
    "path": "addons/guide/editor/input_display/input_display.gd.uid",
    "content": "uid://cgf2qrodwja32\n"
  },
  {
    "path": "addons/guide/editor/input_display/input_display.tscn",
    "content": "[gd_scene load_steps=3 format=3 uid=\"uid://dsv7s6tfmnsrs\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/editor/input_display/input_display.gd\" id=\"1_ne6sd\"]\n\n[sub_resource type=\"StyleBoxEmpty\" id=\"StyleBoxEmpty_0bp65\"]\n\n[node name=\"InputDisplay\" type=\"RichTextLabel\"]\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\ntheme_override_styles/normal = SubResource(\"StyleBoxEmpty_0bp65\")\nbbcode_enabled = true\nfit_content = true\nscript = ExtResource(\"1_ne6sd\")\n"
  },
  {
    "path": "addons/guide/editor/input_mapping_editor/input_mapping_editor.gd",
    "content": "@tool\nextends MarginContainer\n\nconst ArrayEdit = preload(\"../array_edit/array_edit.gd\")\nconst ClassScanner = preload(\"../class_scanner.gd\")\nconst Utils = preload(\"../utils.gd\")\n\n@export var modifier_slot_scene:PackedScene\n@export var trigger_slot_scene:PackedScene\n@export var binding_dialog_scene:PackedScene\n\n@onready var _edit_input_mapping_button:Button = %EditInputMappingButton\n@onready var _input_display:RichTextLabel = %InputDisplay\n@onready var _edit_input_button:Button = %EditInputButton\n@onready var _clear_input_button:Button = %ClearInputButton\n\n@onready var _modifiers:ArrayEdit = %Modifiers\n@onready var _add_modifier_popup:PopupMenu = %AddModifierPopup\n\n@onready var _triggers:ArrayEdit = %Triggers\n@onready var _add_trigger_popup:PopupMenu = %AddTriggerPopup\n\nvar _plugin:EditorPlugin\nvar _undo_redo:EditorUndoRedoManager\n\nvar _mapping:GUIDEInputMapping\n\nfunc _ready():\n\t_edit_input_button.icon = get_theme_icon(\"Edit\", \"EditorIcons\")\n\t_clear_input_button.icon = get_theme_icon(\"Remove\", \"EditorIcons\")\n\t_edit_input_mapping_button.icon = get_theme_icon(\"Tools\", \"EditorIcons\")\n\t\n\t_modifiers.add_requested.connect(_on_modifiers_add_requested)\n\t_modifiers.delete_requested.connect(_on_modifier_delete_requested)\n\t_modifiers.duplicate_requested.connect(_on_modifier_duplicate_requested)\n\t_modifiers.move_requested.connect(_on_modifier_move_requested)\n\t_modifiers.clear_requested.connect(_on_modifiers_clear_requested)\n\t_modifiers.collapse_state_changed.connect(_on_modifiers_collapse_state_changed)\n\t\n\t_triggers.add_requested.connect(_on_triggers_add_requested)\n\t_triggers.delete_requested.connect(_on_trigger_delete_requested)\n\t_triggers.duplicate_requested.connect(_on_trigger_duplicate_requested)\n\t_triggers.move_requested.connect(_on_trigger_move_requested)\n\t_triggers.clear_requested.connect(_on_triggers_clear_requested)\n\t_triggers.collapse_state_changed.connect(_on_triggers_collapse_state_changed)\n\t\n\t\nfunc initialize(plugin:EditorPlugin) -> void:\n\t_plugin = plugin\n\t_undo_redo = plugin.get_undo_redo()\n\t_input_display.clicked.connect(_on_input_display_clicked)\n\t\n\t\nfunc edit(mapping:GUIDEInputMapping) -> void:\n\tassert(_mapping == null)\n\t_mapping = mapping\n\t_mapping.changed.connect(_update)\n\t_update()\n\t\n\t\nfunc _update() -> void:\n\t_modifiers.clear()\n\t_triggers.clear()\n\t\n\t_input_display.input = _mapping.input\n\tfor i in _mapping.modifiers.size():\n\t\tvar modifier_slot := modifier_slot_scene.instantiate()\n\t\t_modifiers.add_item(modifier_slot)\n\n\t\tmodifier_slot.modifier = _mapping.modifiers[i]\n\t\tmodifier_slot.changed.connect(_on_modifier_changed.bind(i, modifier_slot))\n\t\t\n\tfor i in _mapping.triggers.size():\n\t\tvar trigger_slot := trigger_slot_scene.instantiate()\n\t\t_triggers.add_item(trigger_slot)\n\n\t\ttrigger_slot.trigger = _mapping.triggers[i]\n\t\ttrigger_slot.changed.connect(_on_trigger_changed.bind(i, trigger_slot))\n\t\t\n\t_modifiers.collapsed = _mapping.get_meta(\"_guide_modifiers_collapsed\", false)\n\t_triggers.collapsed = _mapping.get_meta(\"_guide_triggers_collapsed\", false)\n\t\n\nfunc _on_modifiers_add_requested():\n\t_fill_popup(_add_modifier_popup, \"GUIDEModifier\")\n\t_add_modifier_popup.popup(Rect2(get_screen_position() + get_local_mouse_position(), Vector2.ZERO))\n\n\nfunc _on_triggers_add_requested():\n\t_fill_popup(_add_trigger_popup, \"GUIDETrigger\")\n\t_add_trigger_popup.popup(Rect2(get_screen_position() + get_local_mouse_position(), Vector2.ZERO))\n\t\n\t\nfunc _fill_popup(popup:PopupMenu, base_clazz:StringName):\n\tpopup.clear(true)\n\t\n\tvar inheritors := ClassScanner.find_inheritors(base_clazz)\n\tfor type in inheritors.keys():\n\t\tvar class_script:Script = inheritors[type]\n\t\tvar dummy:Variant = class_script.new()\n\t\tpopup.add_item(dummy._editor_name())\n\t\tpopup.set_item_tooltip(popup.item_count -1, dummy._editor_description())\n\t\tpopup.set_item_metadata(popup.item_count - 1, class_script)\n\nfunc _on_input_display_clicked():\n\tif is_instance_valid(_mapping.input):\n\t\tEditorInterface.edit_resource(_mapping.input)\n\n\nfunc _on_input_changed(input:GUIDEInput):\n\t_undo_redo.create_action(\"Change input\")\n\t\n\t_undo_redo.add_do_property(_mapping, \"input\", input)\n\t_undo_redo.add_undo_property(_mapping, \"input\", _mapping.input)\n\t\n\t_undo_redo.commit_action()\n\t\n\tif is_instance_valid(input):\n\t\tEditorInterface.edit_resource(input)\n\t\n\nfunc _on_edit_input_button_pressed():\n\tvar dialog:Window = binding_dialog_scene.instantiate()\n\tEditorInterface.popup_dialog_centered(dialog)\t\n\tdialog.initialize()\n\tdialog.input_selected.connect(_on_input_changed)\n\n\nfunc _on_clear_input_button_pressed():\n\t_undo_redo.create_action(\"Delete bound input\")\n\t\n\t_undo_redo.add_do_property(_mapping, \"input\", null)\n\t_undo_redo.add_undo_property(_mapping, \"triggers\", _mapping.input)\n\t\n\t_undo_redo.commit_action()\n\n\nfunc _on_add_modifier_popup_index_pressed(index:int) -> void:\n\tvar script = _add_modifier_popup.get_item_metadata(index)\n\tvar new_modifier = script.new()\n\t\n\t_undo_redo.create_action(\"Add \" + new_modifier._editor_name() + \" modifier\")\n\tvar modifiers := _mapping.modifiers.duplicate()\n\tmodifiers.append(new_modifier)\n\t\n\t_undo_redo.add_do_property(_mapping, \"modifiers\", modifiers)\n\t_undo_redo.add_undo_property(_mapping, \"modifiers\", _mapping.modifiers)\n\t\n\t_undo_redo.commit_action()\n\n\nfunc _on_add_trigger_popup_index_pressed(index):\n\tvar script = _add_trigger_popup.get_item_metadata(index)\n\tvar new_trigger = script.new()\n\t\n\t_undo_redo.create_action(\"Add \" + new_trigger._editor_name() + \" trigger\")\n\tvar triggers := _mapping.triggers.duplicate()\n\ttriggers.append(new_trigger)\n\t\n\t_undo_redo.add_do_property(_mapping, \"triggers\", triggers)\n\t_undo_redo.add_undo_property(_mapping, \"triggers\", _mapping.triggers)\n\t\n\t_undo_redo.commit_action()\n\n\nfunc _on_modifier_changed(index:int, slot) -> void:\n\tvar new_modifier = slot.modifier\n\t\n\t_undo_redo.create_action(\"Replace modifier\")\n\tvar modifiers := _mapping.modifiers.duplicate()\n\tmodifiers[index] = new_modifier\n\t\n\t_undo_redo.add_do_property(_mapping, \"modifiers\", modifiers)\n\t_undo_redo.add_undo_property(_mapping, \"modifiers\", _mapping.modifiers)\n\t\n\t_undo_redo.commit_action()\n\t\n\t\nfunc _on_trigger_changed(index:int, slot) -> void:\n\tvar new_trigger = slot.trigger\n\t\n\t_undo_redo.create_action(\"Replace trigger\")\n\tvar triggers := _mapping.triggers.duplicate()\n\ttriggers[index] = new_trigger\n\t\n\t_undo_redo.add_do_property(_mapping, \"triggers\", triggers)\n\t_undo_redo.add_undo_property(_mapping, \"triggers\", _mapping.triggers)\n\t\n\t_undo_redo.commit_action()\n\t\n\t\nfunc _on_modifier_move_requested(from:int, to:int) -> void:\n\t_undo_redo.create_action(\"Move modifier\")\n\tvar modifiers := _mapping.modifiers.duplicate()\n\tvar modifier = modifiers[from]\n\tmodifiers.remove_at(from)\n\tif from < to:\n\t\tto -= 1\n\tmodifiers.insert(to, modifier)\n\t\n\t_undo_redo.add_do_property(_mapping, \"modifiers\", modifiers)\n\t_undo_redo.add_undo_property(_mapping, \"modifiers\", _mapping.modifiers)\n\t\n\t_undo_redo.commit_action()\n\n\nfunc _on_trigger_move_requested(from:int, to:int) -> void:\n\t_undo_redo.create_action(\"Move trigger\")\n\tvar triggers := _mapping.triggers.duplicate()\n\tvar trigger:GUIDETrigger = triggers[from]\n\ttriggers.remove_at(from)\n\tif from < to:\n\t\tto -= 1\n\ttriggers.insert(to, trigger)\n\t\n\t_undo_redo.add_do_property(_mapping, \"triggers\", triggers)\n\t_undo_redo.add_undo_property(_mapping, \"triggers\", _mapping.triggers)\n\t\n\t_undo_redo.commit_action()\n\nfunc _on_modifier_duplicate_requested(index:int) -> void:\n\t_undo_redo.create_action(\"Duplicate modifier\")\n\tvar modifiers := _mapping.modifiers.duplicate()\n\tvar copy := Utils.duplicate_if_inline(modifiers[index])\n\tmodifiers.insert(index+1, copy)\n\t\n\t_undo_redo.add_do_property(_mapping, \"modifiers\", modifiers)\n\t_undo_redo.add_undo_property(_mapping, \"modifiers\", _mapping.modifiers)\n\t\n\t_undo_redo.commit_action()\t\t\n\nfunc _on_trigger_duplicate_requested(index:int) -> void:\n\t_undo_redo.create_action(\"Duplicate trigger\")\n\tvar triggers := _mapping.triggers.duplicate()\n\tvar copy := Utils.duplicate_if_inline(triggers[index])\n\ttriggers.insert(index+1, copy)\n\t\n\t_undo_redo.add_do_property(_mapping, \"triggers\", triggers)\n\t_undo_redo.add_undo_property(_mapping, \"triggers\", _mapping.triggers)\n\t\n\t_undo_redo.commit_action()\t\n\n\n\nfunc _on_modifier_delete_requested(index:int) -> void:\n\t_undo_redo.create_action(\"Delete modifier\")\n\tvar modifiers := _mapping.modifiers.duplicate()\n\tmodifiers.remove_at(index)\n\t\n\t_undo_redo.add_do_property(_mapping, \"modifiers\", modifiers)\n\t_undo_redo.add_undo_property(_mapping, \"modifiers\", _mapping.modifiers)\n\t\n\t_undo_redo.commit_action()\t\t\n\n\t\nfunc _on_trigger_delete_requested(index:int) -> void:\n\t_undo_redo.create_action(\"Delete trigger\")\n\tvar triggers := _mapping.triggers.duplicate()\n\ttriggers.remove_at(index)\n\t\n\t_undo_redo.add_do_property(_mapping, \"triggers\", triggers)\n\t_undo_redo.add_undo_property(_mapping, \"triggers\", _mapping.triggers)\n\t\n\t_undo_redo.commit_action()\t\n\t\n\nfunc _on_modifiers_clear_requested() -> void:\n\t_undo_redo.create_action(\"Clear modifiers\")\n\t# if this is inlined into the do_property, then it doesn't work\n\t# so lets keep it a local variable\n\tvar value:Array[GUIDEModifier] = []\n\t_undo_redo.add_do_property(_mapping, \"modifiers\", value)\n\t_undo_redo.add_undo_property(_mapping, \"modifiers\", _mapping.modifiers)\n\t\n\t_undo_redo.commit_action()\t\n\n\nfunc _on_triggers_clear_requested() -> void:\n\t_undo_redo.create_action(\"Clear triggers\")\n\t# if this is inlined into the do_property, then it doesn't work\n\t# so lets keep it a local variable\n\tvar value:Array[GUIDETrigger] = []\n\t_undo_redo.add_do_property(_mapping, \"triggers\", value)\n\t_undo_redo.add_undo_property(_mapping, \"triggers\", _mapping.triggers)\n\t\n\t_undo_redo.commit_action()\t\n\t\n\t\nfunc _on_modifiers_collapse_state_changed(new_state:bool):\n\t_mapping.set_meta(\"_guide_modifiers_collapsed\", new_state)\n\t\nfunc _on_triggers_collapse_state_changed(new_state:bool):\n\t_mapping.set_meta(\"_guide_triggers_collapsed\", new_state)\n\n\nfunc _on_edit_input_mapping_button_pressed():\n\tEditorInterface.edit_resource(_mapping)\n"
  },
  {
    "path": "addons/guide/editor/input_mapping_editor/input_mapping_editor.gd.uid",
    "content": "uid://dsw33iehbw8q6\n"
  },
  {
    "path": "addons/guide/editor/input_mapping_editor/input_mapping_editor.tscn",
    "content": "[gd_scene load_steps=9 format=3 uid=\"uid://c323mdijdhktg\"]\n\n[ext_resource type=\"PackedScene\" uid=\"uid://dsv7s6tfmnsrs\" path=\"res://addons/guide/editor/input_display/input_display.tscn\" id=\"1_pg8n3\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/editor/input_mapping_editor/input_mapping_editor.gd\" id=\"1_xsluc\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://ck5a30syo6bpo\" path=\"res://addons/guide/editor/modifier_slot/modifier_slot.tscn\" id=\"2_uhbrq\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://tk30wnstb0ku\" path=\"res://addons/guide/editor/trigger_slot/trigger_slot.tscn\" id=\"3_e0jys\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://dic27bm4pfw3q\" path=\"res://addons/guide/editor/binding_dialog/binding_dialog.tscn\" id=\"4_oepf3\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://cly0ff32fvpb2\" path=\"res://addons/guide/editor/array_edit/array_edit.tscn\" id=\"6_jekhk\"]\n\n[sub_resource type=\"Image\" id=\"Image_m1w1j\"]\ndata = {\n\"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, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),\n\"format\": \"RGBA8\",\n\"height\": 16,\n\"mipmaps\": false,\n\"width\": 16\n}\n\n[sub_resource type=\"ImageTexture\" id=\"ImageTexture_y0eyy\"]\nimage = SubResource(\"Image_m1w1j\")\n\n[node name=\"InputMappingEditor\" type=\"MarginContainer\"]\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\nsize_flags_vertical = 0\nscript = ExtResource(\"1_xsluc\")\nmodifier_slot_scene = ExtResource(\"2_uhbrq\")\ntrigger_slot_scene = ExtResource(\"3_e0jys\")\nbinding_dialog_scene = ExtResource(\"4_oepf3\")\n\n[node name=\"HBoxContainer\" type=\"HBoxContainer\" parent=\".\"]\nlayout_mode = 2\nsize_flags_vertical = 0\ntheme_override_constants/separation = 8\n\n[node name=\"MarginContainer\" type=\"MarginContainer\" parent=\"HBoxContainer\"]\nlayout_mode = 2\nsize_flags_vertical = 0\n\n[node name=\"Panel\" type=\"Panel\" parent=\"HBoxContainer/MarginContainer\"]\nvisible = false\nlayout_mode = 2\n\n[node name=\"EditInputMappingButton\" type=\"Button\" parent=\"HBoxContainer/MarginContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntooltip_text = \"Open input mapping in inspector\"\nicon = SubResource(\"ImageTexture_y0eyy\")\nflat = true\n\n[node name=\"MarginContainer1\" type=\"MarginContainer\" parent=\"HBoxContainer\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\n\n[node name=\"Panel\" type=\"Panel\" parent=\"HBoxContainer/MarginContainer1\"]\nvisible = false\nlayout_mode = 2\n\n[node name=\"HBoxContainer\" type=\"HBoxContainer\" parent=\"HBoxContainer/MarginContainer1\"]\nlayout_mode = 2\n\n[node name=\"InputDisplay\" parent=\"HBoxContainer/MarginContainer1/HBoxContainer\" instance=ExtResource(\"1_pg8n3\")]\nunique_name_in_owner = true\nlayout_mode = 2\nscroll_active = false\n\n[node name=\"EditInputButton\" type=\"Button\" parent=\"HBoxContainer/MarginContainer1/HBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_vertical = 0\ntooltip_text = \"Edit bound input...\"\nicon = SubResource(\"ImageTexture_y0eyy\")\nflat = true\n\n[node name=\"ClearInputButton\" type=\"Button\" parent=\"HBoxContainer/MarginContainer1/HBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_vertical = 0\ntooltip_text = \"Delete bound input\"\nicon = SubResource(\"ImageTexture_y0eyy\")\nflat = true\n\n[node name=\"MarginContainer2\" type=\"MarginContainer\" parent=\"HBoxContainer\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_stretch_ratio = 2.0\n\n[node name=\"Panel\" type=\"Panel\" parent=\"HBoxContainer/MarginContainer2\"]\nvisible = false\nlayout_mode = 2\n\n[node name=\"VBoxContainer\" type=\"VBoxContainer\" parent=\"HBoxContainer/MarginContainer2\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 0\nsize_flags_stretch_ratio = 2.0\n\n[node name=\"Modifiers\" parent=\"HBoxContainer/MarginContainer2/VBoxContainer\" instance=ExtResource(\"6_jekhk\")]\nunique_name_in_owner = true\nlayout_mode = 2\ntitle = \"Modifiers\"\nadd_tooltip = \"Add modifier...\"\nclear_tooltip = \"Clear modifiers\"\n\n[node name=\"AddModifierPopup\" type=\"PopupMenu\" parent=\"HBoxContainer/MarginContainer2/VBoxContainer\"]\nunique_name_in_owner = true\n\n[node name=\"MarginContainer3\" type=\"MarginContainer\" parent=\"HBoxContainer\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_stretch_ratio = 2.0\n\n[node name=\"Panel\" type=\"Panel\" parent=\"HBoxContainer/MarginContainer3\"]\nvisible = false\nlayout_mode = 2\n\n[node name=\"VBoxContainer2\" type=\"VBoxContainer\" parent=\"HBoxContainer/MarginContainer3\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 0\nsize_flags_stretch_ratio = 2.0\n\n[node name=\"Triggers\" parent=\"HBoxContainer/MarginContainer3/VBoxContainer2\" instance=ExtResource(\"6_jekhk\")]\nunique_name_in_owner = true\nlayout_mode = 2\ntitle = \"Triggers\"\nadd_tooltip = \"Add trigger...\"\nclear_tooltip = \"Clear triggers\"\n\n[node name=\"AddTriggerPopup\" type=\"PopupMenu\" parent=\"HBoxContainer/MarginContainer3/VBoxContainer2\"]\nunique_name_in_owner = true\n\n[connection signal=\"pressed\" from=\"HBoxContainer/MarginContainer/EditInputMappingButton\" to=\".\" method=\"_on_edit_input_mapping_button_pressed\"]\n[connection signal=\"pressed\" from=\"HBoxContainer/MarginContainer1/HBoxContainer/EditInputButton\" to=\".\" method=\"_on_edit_input_button_pressed\"]\n[connection signal=\"pressed\" from=\"HBoxContainer/MarginContainer1/HBoxContainer/ClearInputButton\" to=\".\" method=\"_on_clear_input_button_pressed\"]\n[connection signal=\"index_pressed\" from=\"HBoxContainer/MarginContainer2/VBoxContainer/AddModifierPopup\" to=\".\" method=\"_on_add_modifier_popup_index_pressed\"]\n[connection signal=\"index_pressed\" from=\"HBoxContainer/MarginContainer3/VBoxContainer2/AddTriggerPopup\" to=\".\" method=\"_on_add_trigger_popup_index_pressed\"]\n"
  },
  {
    "path": "addons/guide/editor/logo_editor_small.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cap7e0f05pj8j\"\npath=\"res://.godot/imported/logo_editor_small.svg-a18f1eaff840dcdf5215ef26c289caf9.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/editor/logo_editor_small.svg\"\ndest_files=[\"res://.godot/imported/logo_editor_small.svg-a18f1eaff840dcdf5215ef26c289caf9.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=0.5\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/editor/mapping_context_editor/mapping_context_editor.gd",
    "content": "@tool\nextends MarginContainer\n\nconst ClassScanner = preload(\"../class_scanner.gd\")\nconst ResourceScanner = preload(\"../resource_scanner.gd\")\nconst Utils = preload(\"../utils.gd\")\nconst ArrayEdit = preload(\"../array_edit/array_edit.gd\")\n\n@export var action_mapping_editor_scene:PackedScene\n\n@onready var _action_mappings:ArrayEdit = %ActionMappings\n@onready var _editing_view:Control = %EditingView\n@onready var _empty_view:Control = %EmptyView\n@onready var _mapping_context_switcher:OptionButton = %MappingContextSwitcher\n\nvar _plugin:EditorPlugin\nvar _current_context:GUIDEMappingContext\nvar _undo_redo:EditorUndoRedoManager\nvar _list_dirty:bool\n\nfunc _ready() -> void:\n\tif Utils.is_node_in_edited_scene(self):\n\t\treturn\n\t\n\t_editing_view.visible = false\n\t_empty_view.visible = true\n\t_action_mappings.show_insert_options = true\n\t_action_mappings.add_requested.connect(_on_action_mappings_add_requested)\n\t_action_mappings.insert_requested.connect(_on_action_mappings_insert_requested)\n\t_action_mappings.move_requested.connect(_on_action_mappings_move_requested)\n\t_action_mappings.delete_requested.connect(_on_action_mapping_delete_requested)\n\t_action_mappings.clear_requested.connect(_on_action_mappings_clear_requested)\n\t_action_mappings.duplicate_requested.connect(_on_action_mapping_duplicate_requested)\n\t_action_mappings.collapse_state_changed.connect(_on_action_mappings_collapse_state_changed)\n\t\n\t_mapping_context_switcher.item_selected.connect(_on_mapping_context_switch_requested)\n\t\n\t_list_dirty = true\n\tvisibility_changed.connect(func() -> void: if visible: _update_list())\n\t\n\nfunc initialize(plugin:EditorPlugin) -> void:\n\t_plugin = plugin\n\t_undo_redo = plugin.get_undo_redo()\n\t# mark the list dirty in case the fiqddle system changed.\n\tEditorInterface.get_resource_filesystem().filesystem_changed.connect(func() -> void: _list_dirty = true)\n\t\n\t\nfunc edit(context:GUIDEMappingContext) -> void:\n\tif is_instance_valid(_current_context):\n\t\t_current_context.changed.disconnect(_refresh)\n\t\t\n\t_current_context = context\n\t\n\tif is_instance_valid(_current_context):\n\t\t_current_context.changed.connect(_refresh)\n\t\n\t_refresh()\n\t\n\t\nfunc _update_list() -> void:\n\tif not _list_dirty:\n\t\treturn\n\t\t\n\tvar context_paths:Array[String] = ResourceScanner.find_resources_of_type(\"GUIDEMappingContext\")\n\t## Build a mapping of shortened labels -> full paths and populate the switcher\n\tvar shortened:Dictionary = _build_shortened_path_map(context_paths)\n\t_mapping_context_switcher.clear()\n\n\t# Insert a dummy entry at the top so the user can intentionally pick a context.\n\tvar dummy_label:String = \"Select mapping context...\"\n\t_mapping_context_switcher.add_item(dummy_label)\n\t# Metadata null marks the dummy entry.\n\t_mapping_context_switcher.set_item_metadata(0, null)\n\n\tvar keys:Array[String] = []\n\tfor k in shortened.keys():\n\t\tkeys.append(k as String)\n\tkeys.sort()\n\n\tfor i in keys.size():\n\t\tvar key:String = keys[i]\n\t\tvar full_path:String = shortened[key]\n\t\t# +1 because of the dummy item at index 0\n\t\tvar render_index:int = i + 1\n\t\t_mapping_context_switcher.add_item(key)\n\t\t_mapping_context_switcher.set_item_metadata(render_index, full_path)\n\t\t_mapping_context_switcher.set_item_tooltip(render_index, full_path)\n\t\n\t_list_dirty = false\n\n\t\nfunc _refresh() -> void:\n\t_update_list()\n\t\n\t_editing_view.visible = is_instance_valid(_current_context)\n\t_empty_view.visible = not is_instance_valid(_current_context)\n\t\n\n\tif not is_instance_valid(_current_context):\n\t\treturn\n\n\t# make sure the switcher shows the currently selected item.\n\t_mapping_context_switcher.tooltip_text = _current_context.resource_path\n\tvar selected_index:int = 0\n\tfor i in _mapping_context_switcher.item_count:\n\t\tvar item_path = _mapping_context_switcher.get_item_metadata(i)\n\t\tif item_path == _current_context.resource_path:\n\t\t\tselected_index = i\n\t\t\tbreak\n\t\t\t\n\t_mapping_context_switcher.select(selected_index)\n\t\n\t_action_mappings.clear()\n\t\t\n\tfor i in _current_context.mappings.size():\n\t\tvar mapping := _current_context.mappings[i]\n\t\t\n\t\tvar mapping_editor:Variant = action_mapping_editor_scene.instantiate()\n\t\tmapping_editor.initialize(_plugin)\n\t\t\n\t\t_action_mappings.add_item(mapping_editor)\n\t\t\n\t\tmapping_editor.edit(mapping)\n\t\t\n\t_action_mappings.collapsed = _current_context.get_meta(\"_guide_action_mappings_collapsed\", false)\n\n\n## Creates unique, human-friendly short labels for a list of resource paths by\n## progressively adding parent directories to conflicting filenames until they\n## become unique. The resulting keys are sorted elsewhere for display.\n## @param paths All full resource paths to mapping contexts (e.g. res://foo/bar/baz.tres).\n## @return Dictionary that maps a shortened label (e.g. \"bar/baz.tres\") to the full path.\nfunc _build_shortened_path_map(paths:Array[String]) -> Dictionary:\n\t# Prepare path parts reversed per path for easy prefix growth from filename upwards.\n\t# Note: Avoid nested typed collections (Array[Array[String]]) due to GDScript limitations.\n\tvar parts_per_path:Array = []\n\tparts_per_path.resize(paths.size())\n\tfor i in paths.size():\n\t\tvar p:String = paths[i]\n\t\tvar without_scheme:String = p\n\t\tif without_scheme.begins_with(\"res://\"):\n\t\t\twithout_scheme = without_scheme.substr(6)\n\t\tvar comps:Array = without_scheme.split(\"/\", false)\n\t\tcomps.reverse() # [filename, parent, grandparent, ...]\n\t\tparts_per_path[i] = comps\n\n\t# Track how many components from the end each entry currently uses for its label.\n\tvar used_counts:Array[int] = []\n\tused_counts.resize(paths.size())\n\tfor i in used_counts.size():\n\t\tused_counts[i] = 1 # start with filename only\n\n\tvar current_label:Callable = func(idx:int) -> String:\n\t\tvar parts:Array = parts_per_path[idx]\n\t\tvar count:int = min(used_counts[idx], parts.size())\n\t\tvar slice:Array = parts.slice(0, count)\n\t\tslice.reverse() # restore order to parent/.../filename\n\t\treturn \"/\".join(slice)\n\n\t# Iteratively resolve collisions by adding parents to all entries in each clashing group.\n\twhile true:\n\t\tvar label_to_indices:Dictionary = {}\n\t\tfor i in paths.size():\n\t\t\tvar label:String = current_label.call(i)\n\t\t\tif not label_to_indices.has(label):\n\t\t\t\tlabel_to_indices[label] = []\n\t\t\t(label_to_indices[label] as Array).append(i)\n\t\t\n\t\tvar had_collision:bool = false\n\t\tfor label in label_to_indices.keys():\n\t\t\tvar indices:Array = label_to_indices[label]\n\t\t\tif indices.size() > 1:\n\t\t\t\thad_collision = true\n\t\t\t\tfor idx in indices:\n\t\t\t\t\tvar i:int = int(idx)\n\t\t\t\t\t# Only increase if there are more parents available.\n\t\t\t\t\tif used_counts[i] < parts_per_path[i].size():\n\t\t\t\t\t\tused_counts[i] += 1\n\t\t\t\t\t# If exhausted, we leave it as full path-equivalent label.\n\t\t\n\t\tif not had_collision:\n\t\t\tbreak\n\n\t# Build the final dictionary mapping label -> full path.\n\tvar result:Dictionary = {}\n\tfor i in paths.size():\n\t\tvar label:String = current_label.call(i)\n\t\tresult[label] = paths[i]\n\treturn result\n\t\n\t\n\t\t\n\t\t\nfunc _on_mapping_context_switch_requested(index:int) -> void:\n\tvar mc:Variant = _mapping_context_switcher.get_item_metadata(index)\n\tif mc != null:\n\t\tvar context:GUIDEMappingContext = load(mc as String) as GUIDEMappingContext\n\t\tif context != null and context != _current_context:\n\t\t\tedit(context)\n\t\n\t\t\nfunc _on_action_mappings_add_requested() -> void:\n\tvar mappings := _current_context.mappings.duplicate()\n\tvar new_mapping := GUIDEActionMapping.new()\n\t# don't set an action because they should come from the file system\n\tmappings.append(new_mapping)\n\t\n\t_undo_redo.create_action(\"Add action mapping\")\n\t\n\t_undo_redo.add_do_property(_current_context, \"mappings\", mappings)\n\t_undo_redo.add_undo_property(_current_context, \"mappings\", _current_context.mappings)\n\t\n\t_undo_redo.commit_action()\n\nfunc _on_action_mappings_insert_requested(index:int) -> void:\n\tvar mappings := _current_context.mappings.duplicate()\n\tvar new_mapping := GUIDEActionMapping.new()\n\t\n\t# don't set an action because they should come from the file system\n\tmappings.insert(index, new_mapping)\n\t\n\t_undo_redo.create_action(\"Insert action mapping\")\n\t\n\t_undo_redo.add_do_property(_current_context, \"mappings\", mappings)\n\t_undo_redo.add_undo_property(_current_context, \"mappings\", _current_context.mappings)\n\t\n\t_undo_redo.commit_action()\n\nfunc _on_action_mappings_move_requested(from:int, to:int) -> void:\n\tvar mappings := _current_context.mappings.duplicate()\n\tvar mapping:Variant = mappings[from]\n\tmappings.remove_at(from)\n\tif from < to:\n\t\tto -= 1\n\tmappings.insert(to, mapping)\n\t\n\t_undo_redo.create_action(\"Move action mapping\")\n\t\n\t_undo_redo.add_do_property(_current_context, \"mappings\", mappings)\n\t_undo_redo.add_undo_property(_current_context, \"mappings\", _current_context.mappings)\n\t\n\t_undo_redo.commit_action()\n\n\nfunc _on_action_mapping_delete_requested(index:int) -> void:\n\tvar mappings := _current_context.mappings.duplicate()\n\tmappings.remove_at(index)\n\t\n\t_undo_redo.create_action(\"Delete action mapping\")\n\t\n\t_undo_redo.add_do_property(_current_context, \"mappings\", mappings)\n\t_undo_redo.add_undo_property(_current_context, \"mappings\", _current_context.mappings)\n\t\n\t_undo_redo.commit_action()\n\n\nfunc _on_action_mappings_clear_requested() -> void:\n\tvar mappings:Array[GUIDEActionMapping] = []\n\t\n\t_undo_redo.create_action(\"Clear action mappings\")\n\t\n\t_undo_redo.add_do_property(_current_context, \"mappings\", mappings)\n\t_undo_redo.add_undo_property(_current_context, \"mappings\", _current_context.mappings)\n\t\n\t_undo_redo.commit_action()\n\t\nfunc _on_action_mapping_duplicate_requested(index:int) -> void:\n\tvar mappings := _current_context.mappings.duplicate()\n\tvar to_duplicate:GUIDEActionMapping = mappings[index]\n\t\n\tvar copy := GUIDEActionMapping.new()\n\t# don't set the action, because each mapping should have a unique mapping\n\tfor input_mapping:GUIDEInputMapping in to_duplicate.input_mappings:\n\t\tvar copied_input_mapping := GUIDEInputMapping.new()\n\t\tcopied_input_mapping.input = Utils.duplicate_if_inline(input_mapping.input)\n\t\tfor modifier in input_mapping.modifiers:\n\t\t\tcopied_input_mapping.modifiers.append(Utils.duplicate_if_inline(modifier))\n\t\t\n\t\tfor trigger in input_mapping.triggers:\n\t\t\tcopied_input_mapping.triggers.append(Utils.duplicate_if_inline(trigger))\n\t\t\t\n\t\tcopy.input_mappings.append(copied_input_mapping)\n\t\t\t\n\t# insert the copy after the copied mapping\n\tmappings.insert(index+1, copy)\n\t\n\t\n\t_undo_redo.create_action(\"Duplicate action mapping\")\n\t\n\t_undo_redo.add_do_property(_current_context, \"mappings\", mappings)\n\t_undo_redo.add_undo_property(_current_context, \"mappings\", _current_context.mappings)\n\t\n\t_undo_redo.commit_action()\t\n\nfunc _on_action_mappings_collapse_state_changed(new_state:bool) -> void:\n\t_current_context.set_meta(\"_guide_action_mappings_collapsed\", new_state)\n\n\n"
  },
  {
    "path": "addons/guide/editor/mapping_context_editor/mapping_context_editor.gd.uid",
    "content": "uid://bpemf1ch2011g\n"
  },
  {
    "path": "addons/guide/editor/mapping_context_editor/mapping_context_editor.tscn",
    "content": "[gd_scene load_steps=4 format=3 uid=\"uid://dm3hott3tfvwe\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/editor/mapping_context_editor/mapping_context_editor.gd\" id=\"1_vytdu\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://361aipcef24h\" path=\"res://addons/guide/editor/action_mapping_editor/action_mapping_editor.tscn\" id=\"2_qb3p8\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://cly0ff32fvpb2\" path=\"res://addons/guide/editor/array_edit/array_edit.tscn\" id=\"3_x7h5x\"]\n\n[node name=\"MappingContextEditor\" type=\"MarginContainer\"]\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\ntheme_override_constants/margin_left = 5\ntheme_override_constants/margin_top = 5\ntheme_override_constants/margin_right = 5\ntheme_override_constants/margin_bottom = 5\nscript = ExtResource(\"1_vytdu\")\naction_mapping_editor_scene = ExtResource(\"2_qb3p8\")\n\n[node name=\"Toolbar\" type=\"VBoxContainer\" parent=\".\"]\nlayout_mode = 2\n\n[node name=\"Toolbar\" type=\"HBoxContainer\" parent=\"Toolbar\"]\nlayout_mode = 2\n\n[node name=\"Spacer\" type=\"Control\" parent=\"Toolbar/Toolbar\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\n\n[node name=\"Label\" type=\"Label\" parent=\"Toolbar/Toolbar\"]\nlayout_mode = 2\ntext = \"Mapping context:\"\n\n[node name=\"MappingContextSwitcher\" type=\"OptionButton\" parent=\"Toolbar/Toolbar\"]\nunique_name_in_owner = true\nlayout_mode = 2\n\n[node name=\"EditingView\" type=\"VBoxContainer\" parent=\"Toolbar\"]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\nsize_flags_vertical = 3\n\n[node name=\"MarginContainer\" type=\"MarginContainer\" parent=\"Toolbar/EditingView\"]\nlayout_mode = 2\ntheme_override_constants/margin_bottom = 5\n\n[node name=\"ScrollContainer\" type=\"ScrollContainer\" parent=\"Toolbar/EditingView\"]\nlayout_mode = 2\nsize_flags_vertical = 3\n\n[node name=\"ActionMappings\" parent=\"Toolbar/EditingView/ScrollContainer\" instance=ExtResource(\"3_x7h5x\")]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 3\ntitle = \"Action mappings\"\nadd_tooltip = \"Add action mapping\"\nclear_tooltip = \"Clear action mappings\"\n\n[node name=\"EmptyView\" type=\"CenterContainer\" parent=\"Toolbar\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_vertical = 3\n\n[node name=\"Label\" type=\"Label\" parent=\"Toolbar/EmptyView\"]\nlayout_mode = 2\ntext = \"Create and open a GUIDEMappingContext to get started.\"\n"
  },
  {
    "path": "addons/guide/editor/modifier_slot/modifier_slot.gd",
    "content": "@tool\nextends \"../resource_slot/resource_slot.gd\"\n\nvar modifier:GUIDEModifier:\n\tset(value):\n\t\t_value = value\n\tget:\n\t\treturn _value\n\nfunc _accepts_drop_data(data:Resource) -> bool:\n\treturn data is GUIDEModifier\n\t\n\n\n"
  },
  {
    "path": "addons/guide/editor/modifier_slot/modifier_slot.gd.uid",
    "content": "uid://cmvfuu8u5ubkk\n"
  },
  {
    "path": "addons/guide/editor/modifier_slot/modifier_slot.tscn",
    "content": "[gd_scene load_steps=2 format=3 uid=\"uid://ck5a30syo6bpo\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/editor/modifier_slot/modifier_slot.gd\" id=\"1_273m5\"]\n\n[node name=\"LineEdit\" type=\"LineEdit\"]\noffset_right = 1920.0\noffset_bottom = 31.0\nsize_flags_horizontal = 3\nsize_flags_vertical = 0\ntext = \"Name\"\neditable = false\ncontext_menu_enabled = false\nvirtual_keyboard_enabled = false\nshortcut_keys_enabled = false\nmiddle_mouse_paste_enabled = false\nselecting_enabled = false\ndrag_and_drop_selection_enabled = false\nscript = ExtResource(\"1_273m5\")\n"
  },
  {
    "path": "addons/guide/editor/resource_scanner.gd",
    "content": "@tool\n\nconst ClassScanner = preload(\"class_scanner.gd\")\n\n\nstatic var _regex:RegEx\n\nstatic func _static_init():\n\t_regex = RegEx.new()\n\t_regex.compile(\"gd_resource.*?script_class=\\\"([^\\\"]+)\\\"\")\t\t\t\n\n## Finds all resources in the editor that are of the given type.\n## Returns a list of full path names of the found resources.\nstatic func find_resources_of_type(type_name:StringName) -> Array[String]:\n\tvar begin := Time.get_ticks_usec()\n\tvar result:Array[String] = []\n\tvar inheritors:Array = [ type_name ] + ClassScanner.find_inheritors(type_name).keys()\n\t\n\t_find_resources_of_type_in(EditorInterface.get_resource_filesystem().get_filesystem(), inheritors, result)\n\tvar end := Time.get_ticks_usec()\n#\tprint(\"Scan took \", (end - begin) , \"us.\")\n\treturn result\n\nstatic func _find_resources_of_type_in(directory: EditorFileSystemDirectory, inheritors:Array, result:Array[String]):\n\tfor i in directory.get_subdir_count():\n\t\t_find_resources_of_type_in(directory.get_subdir(i), inheritors, result)\n\t\tpass\n\t\n\tfor i in directory.get_file_count():\n\t\tvar path:String = directory.get_file_path(i)\t\n\t\tvar engine_type:StringName = directory.get_file_type(i)\n\t\tvar actual_type:StringName = engine_type\n\t\tif engine_type == \"Resource\":\n\t\t\tvar resource_script_class := _get_resource_script_class(path)\n\t\t\tif resource_script_class != \"\":\n\t\t\t\tactual_type = resource_script_class\n\t\t\t\t\n\t\t# print(path, \" -> \", engine_type, \"-->\" ,actual_type)\n\t\t\n\t\tif inheritors.has(actual_type):\n\t\t\tresult.append(path)\n\n\t\t\nstatic func _get_resource_script_class(path:String) -> String:\n\tif not path.ends_with(\".tres\"):\n\t\treturn \"\"\n\n\t# this is really shoddy as it causes a lot of IO. but the get_resource_script_class is not exposed\n\t# on EditorFileSystemDirectory, so we gotta make do with this.\n\tvar content\t= FileAccess.get_file_as_string(path)\n\tif content == \"\":\n\t\treturn \"\"\n\t\t\n\tvar re_match:RegExMatch = _regex.search(content)\n\tif re_match == null:\n\t\treturn \"\"\n\t\t\n\treturn re_match.get_string(1)\n\n"
  },
  {
    "path": "addons/guide/editor/resource_scanner.gd.uid",
    "content": "uid://bjkbg2epolfg2\n"
  },
  {
    "path": "addons/guide/editor/resource_slot/resource_slot.gd",
    "content": "@tool\nextends LineEdit\n\nsignal changed()\nconst Utils = preload(\"../utils.gd\")\n\nfunc _ready():\n\teditable = false\n\tcontext_menu_enabled = false\n\tvirtual_keyboard_enabled = false\n\tshortcut_keys_enabled = false\n\tselecting_enabled = false\n\tdrag_and_drop_selection_enabled = false\n\tmiddle_mouse_paste_enabled = false\n\n## The underlying resource. This is opened for editing when the user clicks on the control. Its also \n## used when dragging from the control.\nvar _value:Resource = null:\n\tset(value):\n\t\tif _value == value:\n\t\t\treturn\n\n\t\t# stop tracking changes to the old resource (if any)\n\t\tif is_instance_valid(_value):\n\t\t\t_value.changed.disconnect(_update_from_value)\n\t\t\t\n\t\t_value = value\n\n\t\t# track changes to the resource itself\n\t\tif is_instance_valid(_value):\n\t\t\t_value.changed.connect(_update_from_value)\n\t\t\n\t\t_update_from_value()\n\t\tchanged.emit()\n\nfunc _update_from_value():\n\tif not is_instance_valid(_value):\n\t\ttext = \"<none>\"\n\t\ttooltip_text = \"\"\n\t\tremove_theme_color_override(\"font_uneditable_color\")\n\telse:\n\t\ttext = _value._editor_name()\n\t\ttooltip_text = _value.resource_path\n\t\t# if the value is shared, we override the font color to indicate that\n\t\tif not Utils.is_inline(_value):\n\t\t\tadd_theme_color_override(\"font_uneditable_color\", get_theme_color(\"accent_color\", \"Editor\"))\n\t\t\tqueue_redraw()\n\t\telse:\n\t\t\tremove_theme_color_override(\"font_uneditable_color\")\n\n## Can be overridden to handle the drop data. This method is called when the user drops something on the control.\n## If the value should be updated ,this method should set the _value property.\nfunc _do_drop_data(data:Resource):\n\t_value = data\n\t\n\t\n## Whether this control can accept drop data. This method is called when the user drags something over the control.\nfunc _accepts_drop_data(data:Resource) -> bool:\n\treturn false\n\t\nfunc _can_drop_data(at_position, data) -> bool:\n\tif data is Resource:\n\t\treturn _accepts_drop_data(data)\n\t\n\tif not data is Dictionary:\n\t\treturn false\n\n\tif data.has(\"files\"):\n\t\tfor file in data[\"files\"]:\n\t\t\tif _accepts_drop_data(ResourceLoader.load(file)):\n\t\t\t\treturn true\n\n\treturn false\n\n\nfunc _drop_data(at_position, data) -> void:\n\tif data is Resource:\n\t\t_do_drop_data(data)\n\t\treturn\n\t\n\tfor file in data[\"files\"]:\n\t\tvar item := ResourceLoader.load(file)\n\t\t_do_drop_data(item)\n\t\t\n\nfunc _get_drag_data(at_position: Vector2) -> Variant:\n\tif is_instance_valid(_value):\n\t\tvar _preview := TextureRect.new()\n\t\t_preview.texture = get_theme_icon(\"File\", \"EditorIcons\")\n\t\tset_drag_preview(_preview)\n\t\t# if the value is shared, we just hand out the resource path\n\t\tif not Utils.is_inline(_value):\n\t\t\treturn {\"files\": [_value.resource_path]}\n\t\telse:\n\t\t\t# otherwise we hand out a shallow copy\n\t\t\treturn _value.duplicate()\n\telse:\n\t\treturn null\n\nfunc _gui_input(event):\n\tif event is InputEventMouseButton:\n\t\tif event.pressed and event.button_index == MOUSE_BUTTON_LEFT:\n\t\t\tif is_instance_valid(_value):\n\t\t\t\tEditorInterface.edit_resource(_value)\n\n\n"
  },
  {
    "path": "addons/guide/editor/resource_slot/resource_slot.gd.uid",
    "content": "uid://b7cctlhen71jw\n"
  },
  {
    "path": "addons/guide/editor/trigger_slot/trigger_slot.gd",
    "content": "@tool\nextends \"../resource_slot/resource_slot.gd\"\n\nvar trigger:GUIDETrigger:\n\tset(value):\n\t\t_value = value\n\tget:\n\t\treturn _value\n\nfunc _accepts_drop_data(data:Resource) -> bool:\n\treturn data is GUIDETrigger\n\n\t\n\t\n"
  },
  {
    "path": "addons/guide/editor/trigger_slot/trigger_slot.gd.uid",
    "content": "uid://dk2lv53ohhes2\n"
  },
  {
    "path": "addons/guide/editor/trigger_slot/trigger_slot.tscn",
    "content": "[gd_scene load_steps=2 format=3 uid=\"uid://tk30wnstb0ku\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/editor/trigger_slot/trigger_slot.gd\" id=\"1_wxafc\"]\n\n[node name=\"LineEdit\" type=\"LineEdit\"]\nunique_name_in_owner = true\noffset_right = 1920.0\noffset_bottom = 31.0\nsize_flags_horizontal = 3\nsize_flags_vertical = 0\ntooltip_text = \"Delete trigger\"\ntext = \"Name\"\neditable = false\ncontext_menu_enabled = false\nvirtual_keyboard_enabled = false\nshortcut_keys_enabled = false\nmiddle_mouse_paste_enabled = false\nselecting_enabled = false\ndrag_and_drop_selection_enabled = false\nscript = ExtResource(\"1_wxafc\")\n"
  },
  {
    "path": "addons/guide/editor/utils.gd",
    "content": "## Removes and frees all children of a node.\nstatic func clear(node:Node):\n\tif not is_instance_valid(node):\n\t\treturn\n\tfor child in node.get_children():\n\t\tnode.remove_child(child)\n\t\tchild.queue_free()\n\n\n## Checks if the given resource is an inline resource. If so, returns a shallow copy,\n## otherwise returns the resource. If the resource is null, returns null.\nstatic func duplicate_if_inline(resource:Resource) -> Resource:\n\tif is_inline(resource):\n\t\treturn resource.duplicate()\n\treturn resource\n\t\n\n## Checks if the given resource is an inline resource.\nstatic func is_inline(resource:Resource) -> bool:\n\tif resource == null:\n\t\treturn false\n\treturn resource.resource_path.contains(\"::\") or resource.resource_path == \"\"\n\t\n\t\n## Checks if the given node is somewhere in the currently edited scene.\t\nstatic func is_node_in_edited_scene(node:Node) -> bool:\n\tif not Engine.is_editor_hint():\n\t\treturn false\n\t\n\tif not is_instance_valid(node):\n\t\treturn false\n\t\n\tvar editor_interface := Engine.get_singleton(\"EditorInterface\")\n\tif editor_interface == null:\n\t\treturn false\n\tvar scene_root:Node = editor_interface.get_edited_scene_root()\n\tif not is_instance_valid(scene_root):\n\t\treturn false\n\t\t\n\treturn (node == scene_root) or scene_root.is_ancestor_of(node)\n"
  },
  {
    "path": "addons/guide/editor/utils.gd.uid",
    "content": "uid://c2jwjge0gqr1l\n"
  },
  {
    "path": "addons/guide/guide.gd",
    "content": "extends Node\n\nconst GUIDESet = preload(\"guide_set.gd\")\nconst GUIDEReset = preload(\"guide_reset.gd\")\nconst GUIDEInputTracker = preload(\"guide_input_tracker.gd\")\n\n## This is emitted whenever input mappings change (either due to mapping\n## contexts being enabled/disabled or remapping configs being re-applied or\n## joystick devices being connected/disconnected).\n## This is useful for updating UI prompts.\nsignal input_mappings_changed()\n\n## The currently active contexts. Key is the context, value is an array [priority, timestamp_usec].\n## The timestamp is used to break ties when contexts have the same priority - more recently enabled\n## contexts take precedence, allowing later-enabled contexts to merge their inputs with higher priority.\nvar _active_contexts:Dictionary = {}\n## The currently active action mappings.\nvar _active_action_mappings:Array[GUIDEActionMapping] = []\n\n## The currently active remapping config.\nvar _active_remapping_config:GUIDERemappingConfig\n\n## All currently active inputs as collected from the active input mappings\nvar _active_inputs:GUIDESet = GUIDESet.new()\n\n## All currently active modifiers as collected from the active input mappings\nvar _active_modifiers: GUIDESet = GUIDESet.new()\n\n## A dictionary of actions sharing input. Key is the action, value\n## is an array of lower-priority actions that share input with the \n## key action.\nvar _actions_sharing_input:Dictionary = {}\n\n## A reference to the reset node which resets inputs that need a reset per frame\n## This is an extra node because the reset should run at the end of the frame\n## before new input is processed at the beginning of the frame.\nvar _reset_node:GUIDEReset\n\n## The current input state. This is used to track the state of the inputs\n## and serves as a basis for the GUIDEInputs.\nvar _input_state:GUIDEInputState\n\n## A lock, preventing a mapping context change while a mapping\n## context change is currently in progress.\nvar _locked:bool = false\n\nfunc _ready():\n\tprocess_mode = Node.PROCESS_MODE_ALWAYS\n\t_reset_node = GUIDEReset.new()\n\t_input_state = GUIDEInputState.new()\n\t_input_state._reset()\n\tadd_child(_reset_node)\n\t# attach to the current viewport to get input events\n\tGUIDEInputTracker._instrument.call_deferred(get_viewport())\n\t\n\tget_tree().node_added.connect(_on_node_added)\n\t\n\t# Emit a change of input mappings whenever a joystick was connected\n\t# or disconnected.\n\tInput.joy_connection_changed.connect(func(ig, ig2): input_mappings_changed.emit())\n\n\nfunc _notification(what: int) -> void:\n\tif what == NOTIFICATION_APPLICATION_FOCUS_OUT:\n\t\t# When the application loses focus, Godot clears its own input state\n\t\t# directly in Input::release_pressed_events() (core/input/input.cpp)\n\t\t# without dispatching any InputEvents. Since G.U.I.D.E maintains its\n\t\t# own shadow input state in GUIDEInputState, we must replicate this\n\t\t# cleanup ourselves. See: https://github.com/godotneers/G.U.I.D.E/issues/189\n\t\tif _input_state != null:\n\t\t\t_input_state.focus_lost()\n\n\n## Called when a node is added to the tree. If the node is a window\n## GUIDE will instrument it to get events when the window is focused.\nfunc _on_node_added(node:Node) -> void:\n\tif not node is Window:\n\t\treturn\n\t\t\n\tGUIDEInputTracker._instrument(node)\n\t\n\n## Injects input into GUIDE. GUIDE will call this automatically but \n## can also be used to manually inject input for GUIDE to handle \nfunc inject_input(event:InputEvent) -> void:\n\tif event is InputEventAction:\n\t\treturn  # we don't react to Godot's built-in events\n\n\t# The input state is the sole consumer of input events. It will notify\n\t# GUIDEInputs when relevant input events happen. This way we don't need\n\t# to process input events multiple times and at the same time always have\n\t# the full picture of the input state.\n\t_input_state._input(event)\n\n\n## Applies an input remapping config. This will override all input bindings in the \n## currently loaded mapping contexts with the bindings from the configuration.\t\n## Note that GUIDE will not track changes to the remapping config. If your remapping\n## config changes, you will need to call this method again.\nfunc set_remapping_config(config:GUIDERemappingConfig) -> void:\n\t_active_remapping_config = config\n\t_update_caches()\n\t\n\t\n## Enables the given context with the given priority. Lower numbers have higher priority. If \n## disable_others is set to true, all other currently enabled mapping contexts will be disabled.\nfunc enable_mapping_context(context:GUIDEMappingContext, disable_others:bool = false,  priority:int = 0) -> void:\n\tif not is_instance_valid(context):\n\t\tpush_error(\"Null context given. Ignoring.\")\n\t\treturn\n\t\n\tif disable_others:\n\t\tfor enabled_context:GUIDEMappingContext in _active_contexts:\n\t\t\tif enabled_context == context:\n\t\t\t\tcontinue\n\t\t\tenabled_context.disabled.emit()\n\t\t_active_contexts.clear()\t\n\t\n\t_active_contexts[context] = [priority, Time.get_ticks_usec()]\n\t_update_caches()\n\t# notify listeners that the context was enabled\n\tcontext.enabled.emit()\n\t\n\t\n## Disables the given mapping context.\nfunc disable_mapping_context(context:GUIDEMappingContext) -> void:\n\tif not is_instance_valid(context):\n\t\tpush_error(\"Null context given. Ignoring.\")\n\t\treturn\n\n\t_active_contexts.erase(context)\n\t_update_caches()\n\t# notify listeners that the context was disabled\n\tcontext.disabled.emit()\n\n\n## Replaces the currently enabled mapping contexts with a new set of contexts.\n## This is more efficient than calling enable_mapping_context multiple times as it only\n## updates the internal caches once. All contexts are enabled with priority 0, with contexts\n## later in the array having higher precedence when merging inputs (due to later timestamps).\n## Returns the array of contexts that were active before this call.\nfunc set_enabled_mapping_contexts(contexts:Array[GUIDEMappingContext]) -> Array[GUIDEMappingContext]:\n\t# Step 1: Capture previous contexts to return to caller\n\tvar previous_contexts := get_enabled_mapping_contexts()\n\n\t# Step 2: Validate all contexts first (fail fast before modifying state)\n\tfor context:GUIDEMappingContext in contexts:\n\t\tif not is_instance_valid(context):\n\t\t\tpush_error(\"Null context given. Ignoring. Aborting mapping context changes.\")\n\t\t\treturn previous_contexts\n\n\t# Step 3: Save old active contexts for signal emission, then clear\n\t_active_contexts.clear()\n\n\t# Step 4: Add all new contexts with priority=0 and incrementing timestamps\n\t# Start with current timestamp and increment by 1 for each subsequent context\n\t# This ensures contexts later in the array have higher precedence when merging inputs\n\tvar base_timestamp := Time.get_ticks_usec()\n\tfor i:int in contexts.size():\n\t\tvar context := contexts[i]\n\t\t# If context appears multiple times in array, later occurrence wins (higher timestamp)\n\t\t_active_contexts[context] = [0, base_timestamp + i]\n\n\t# Step 5: Update caches\n\t_update_caches()\n\n\t# Step 6: Emit signals for context changes\n\t# Emit disabled for contexts that were active but are not in the new set\n\tfor context:GUIDEMappingContext in previous_contexts:\n\t\tif not _active_contexts.has(context):\n\t\t\tcontext.disabled.emit()\n\n\t# Emit enabled only for contexts that are new (not previously active)\n\tfor context:GUIDEMappingContext in contexts:\n\t\tif not previous_contexts.has(context):\n\t\t\tcontext.enabled.emit()\n\n\t# Step 7: Return previously active contexts\n\treturn previous_contexts\n\n\n## Checks whether the given mapping context is currently enabled.\nfunc is_mapping_context_enabled(context:GUIDEMappingContext) -> bool:\n\treturn _active_contexts.has(context)\n\n\n## Returns the currently enabled mapping contexts\nfunc get_enabled_mapping_contexts() -> Array[GUIDEMappingContext]:\n\tvar result:Array[GUIDEMappingContext] = []\n\tfor key in _active_contexts.keys():\n\t\tresult.append(key)\n\treturn result\n\n## Updates all currently active modifiers\nfunc _physics_process(delta: float) -> void:\n\tfor modifier: GUIDEModifier in _active_modifiers.values():\n\t\tmodifier._physics_process(delta)\n\n## Processes all currently active actions\nfunc _process(delta:float) -> void:\n\tvar blocked_actions:GUIDESet = GUIDESet.new()\n\t\n\tfor action_mapping:GUIDEActionMapping in _active_action_mappings:\n\t\t\n\t\tvar action:GUIDEAction = action_mapping.action\n\t\t\t\t\n\t\t# Walk over all input mappings for this action and consolidate state\n\t\t# and result value.\n\t\tvar consolidated_value:Vector3 = Vector3.ZERO\n\t\tvar consolidated_trigger_state:GUIDETrigger.GUIDETriggerState\n\t\t\n\t\tfor input_mapping:GUIDEInputMapping in action_mapping.input_mappings:\n\t\t\tinput_mapping._update_state(delta, action.action_value_type)\n\t\t\tconsolidated_value += input_mapping._value\n\t\t\tconsolidated_trigger_state = max(consolidated_trigger_state, input_mapping._state)\n\t\t\t# print(\"%s - %s -> %s\" % [Engine.get_process_frames(), consolidated_value, consolidated_trigger_state])\n\t\t\n\t\t# we do the blocking check only here because triggers may need to run anyways\n\t\t# (e.g. to collect hold times).\n\t\tif blocked_actions.has(action):\n\t\t\tconsolidated_trigger_state = GUIDETrigger.GUIDETriggerState.NONE\n\t\t\n\t\tif action.block_lower_priority_actions and \\\n\t\t\tconsolidated_trigger_state == GUIDETrigger.GUIDETriggerState.TRIGGERED and \\\n\t\t\t_actions_sharing_input.has(action):\n\t\t\tfor blocked_action in _actions_sharing_input[action]:\n\t\t\t\tblocked_actions.add(blocked_action)\n\t\t\t\n\t\t\n\t\t# Now state change events.\n\t\tmatch(action._last_state):\n\t\t\tGUIDEAction.GUIDEActionState.TRIGGERED:\n\t\t\t\tmatch(consolidated_trigger_state):\n\t\t\t\t\tGUIDETrigger.GUIDETriggerState.NONE:\n\t\t\t\t\t\taction._completed(consolidated_value)\n\t\t\t\t\tGUIDETrigger.GUIDETriggerState.ONGOING:\n\t\t\t\t\t\taction._ongoing(consolidated_value, delta)\n\t\t\t\t\tGUIDETrigger.GUIDETriggerState.TRIGGERED:\n\t\t\t\t\t\taction._triggered(consolidated_value, delta)\n\t\t\t\t\t\t\n\t\t\tGUIDEAction.GUIDEActionState.ONGOING:\n\t\t\t\tmatch(consolidated_trigger_state):\n\t\t\t\t\tGUIDETrigger.GUIDETriggerState.NONE:\n\t\t\t\t\t\taction._cancelled(consolidated_value)\n\t\t\t\t\tGUIDETrigger.GUIDETriggerState.ONGOING:\n\t\t\t\t\t\taction._ongoing(consolidated_value, delta)\n\t\t\t\t\tGUIDETrigger.GUIDETriggerState.TRIGGERED:\n\t\t\t\t\t\taction._triggered(consolidated_value, delta)\n\t\t\t\t\t\t\n\t\t\tGUIDEAction.GUIDEActionState.COMPLETED:\n\t\t\t\tmatch(consolidated_trigger_state):\n\t\t\t\t\tGUIDETrigger.GUIDETriggerState.NONE:\n\t\t\t\t\t\t# make sure the value updated but don't emit any other events\n\t\t\t\t\t\taction._update_value(consolidated_value)\n\t\t\t\t\tGUIDETrigger.GUIDETriggerState.ONGOING:\n\t\t\t\t\t\taction._started(consolidated_value)\n\t\t\t\t\tGUIDETrigger.GUIDETriggerState.TRIGGERED:\n\t\t\t\t\t\taction._triggered(consolidated_value, delta)\n\n\n## Parses input mappings from a mapping context and adds them to an effective action mapping.\n## This enables multiple mapping contexts to contribute different inputs to the same action\n## (e.g., keyboard context provides WASD while controller context provides joystick for the same \"move\" action).\n## Duplicate inputs are skipped to ensure only one mapping exists per input-action pair, with higher\n## priority contexts taking precedence.\nfunc _parse_input_mappings(\n\t\taction_mapping:GUIDEActionMapping, \n\t\tcontext:GUIDEMappingContext, \n\t\taction:GUIDEAction, \n\t\tnew_inputs:GUIDESet, \n\t\tnew_modifiers:GUIDESet, \n\t\teffective_mapping:GUIDEActionMapping\n\t) -> GUIDEActionMapping:\n\t# the trigger hold threshold is the minimum time that the input must be held\n\t# down before the action triggers. This is used to hint the UI about\n\t# how long the input must be held down. We collect this while iterating\n\t# over the input mappings.\n\tvar trigger_hold_threshold:float = -1.0\n\n\t# now update the action and input mappings\n\tfor index in action_mapping.input_mappings.size():\n\t\tvar input_mapping:GUIDEInputMapping = action_mapping.input_mappings[index]\n\t\t# get the input that is assigned to this action mapping\n\t\tvar bound_input:GUIDEInput = input_mapping.input\n\n\t\t# if the re-mapping has an override for the input (e.g. the player has changed\n\t\t# the default binding to something else), apply it.\n\t\tif _active_remapping_config != null and \\\n\t\t\t\t_active_remapping_config._has(context, action, index):\n\t\t\tbound_input = _active_remapping_config._get_bound_input_or_null(context, action, index)\n\t\t\t# If the remapping explicitly set this to null, it means the input was unbound.\n\t\t\t# Skip adding this input mapping entirely.\n\t\t\tif bound_input == null:\n\t\t\t\tcontinue\n\n\t\t# Consolidate inputs - but only if bound_input is not null (combo mappings can have null inputs)\n\t\tif bound_input != null:\n\t\t\t# check if we already have this kind of input\n\t\t\t# first try to find it in the currently active inputs, this way we don't need to recreate\n\t\t\t# inputs that are already active.\n\t\t\tvar existing:GUIDEInput = _active_inputs.first_match(func(it:GUIDEInput): return it.is_same_as(bound_input))\n\t\t\tif existing == null:\n\t\t\t\t# try to find it in the consolidated inputs\n\t\t\t\texisting = new_inputs.first_match(func(it:GUIDEInput): return it.is_same_as(bound_input))\n\n\t\t\tif existing != null:\n\t\t\t\t# if we already use this input, we can just use the existing one\n\t\t\t\tbound_input = existing\n\n\t\t\t# ensure that the input is initialized and ready to be used\n\t\t\tif not _is_used(bound_input):\n\t\t\t\tbound_input._state = _input_state\n\t\t\t\tbound_input._begin_usage()\n\t\t\t\t_mark_used(bound_input, true)\n\n\t\t\tnew_inputs.add(bound_input)\n\n\t\t# Skip this action and input pair if it has already been defined by a\n\t\t# higher priority mapping context. We check this after remapping and consolidation\n\t\t# to ensure we're comparing the actual input that will be used. We can use == here\n\t\t# because consolidation ensures that inputs that are logically the same share the same object reference.\n\t\tif effective_mapping.input_mappings.any(func(existing): return existing.input == bound_input):\n\t\t\tcontinue\n\n\t\t# make a new input mapping\n\t\tvar new_input_mapping:GUIDEInputMapping = GUIDEInputMapping.new()\n\t\t\t\n\t\t# copy metadata as this may be important for formatting\t\n\t\tnew_input_mapping.input = bound_input\n\t\tnew_input_mapping.display_name = input_mapping.display_name\n\t\tnew_input_mapping.display_category = input_mapping.display_category\n\t\tnew_input_mapping.override_action_settings = input_mapping.override_action_settings\n\t\tnew_input_mapping.is_remappable = input_mapping.is_remappable\n\t\t_copy_meta(input_mapping, new_input_mapping)\n\t\t\n\t\t# modifiers cannot be re-bound so we can just use the one\n\t\t# from the original configuration. this is also needed for shared\n\t\t# modifiers to work.\n\t\tnew_input_mapping.modifiers = input_mapping.modifiers\n\t\t# track the modifiers, so we can later only disable the ones we don't need anymore.\n\t\tfor modifier:GUIDEModifier in new_input_mapping.modifiers:\n\t\t\t# new_modifiers is a set so we can just add the modifier,\n\t\t\t# if we already have it, it will not be added again.\n\t\t\tnew_modifiers.add(modifier)\n\t\t\t\n\t\t\t# initialize the modifier if it is not already in use\n\t\t\tif not _is_used(modifier):\n\t\t\t\tmodifier._begin_usage()\n\t\t\t\t_mark_used(modifier, true)\n\t\t\t\t\n\t\t# triggers also cannot be re-bound but we still make a copy\n\t\t# to ensure that no shared triggers exist.\n\t\tnew_input_mapping.triggers = []\n\t\t\n\t\tfor trigger in input_mapping.triggers:\n\t\t\t# Clone trigger to get a new instance while preserving action references\n\t\t\tnew_input_mapping.triggers.append(trigger.clone())\n\n\t\t# now initialize the input mapping\n\t\tnew_input_mapping._initialize(action.action_value_type)\n\t\t# collect the hold threshold\n\t\tvar mapping_hold_threshold:float = new_input_mapping._trigger_hold_threshold\n\t\t# smallest hold threshold that isn't negative wins\n\t\tif trigger_hold_threshold < 0 or mapping_hold_threshold < trigger_hold_threshold:\n\t\t\ttrigger_hold_threshold = mapping_hold_threshold\n\t\t\n\t\t# and add it to the mapping\n\t\teffective_mapping.input_mappings.append(new_input_mapping)\n\n\t# finally we set the hold threshold for the action\n\t# We need to handle the -1 (uninitialized) case: only use min() when both values are valid\n\tif trigger_hold_threshold >= 0:\n\t\tif action._trigger_hold_threshold < 0:\n\t\t\taction._trigger_hold_threshold = trigger_hold_threshold\n\t\telse:\n\t\t\taction._trigger_hold_threshold = min(action._trigger_hold_threshold, trigger_hold_threshold)\n\n\treturn effective_mapping\n\t\t\t\t\t\t\n## This updates the caches of active inputs, action mappings and modifiers. It's sort of expensive to run\n## but it is only run when contexts are enabled/disabled or remapping configs are applied and it saves\n## a lot of processing time during the actual input processing. It also simplifies the input processing\n## code as all the rules for how inputs, actions and modifiers are consolidated are already applied here.\n## This is called automatically when contexts are enabled/disabled or remapping configs are applied.\nfunc _update_caches() -> void:\n\tif _locked:\n\t\tpush_warning(\"Mapping context changed while processing a context change. Use call_deferred() to change mapping contexts from action signal handlers.\")\n\t\treturn\n\t\n\t_locked = true\t\n\t\n\tvar sorted_contexts:Array[GUIDEMappingContext] = []\n\tsorted_contexts.assign(_active_contexts.keys())\n\t# Sort higher priority,\n\t# then sort later definitions first.\n\tsorted_contexts.sort_custom(func(a,b): return _active_contexts[a][0] < _active_contexts[b][0] or \\\n\t\t(_active_contexts[a][0] == _active_contexts[b][0] and _active_contexts[a][1] > _active_contexts[b][1]))\n\t\n\t# The actions we already have processed. Same action may appear in different\n\t# contexts, so if we find the same action twice, only the first instance wins.\n\tvar processed_actions:GUIDESet = GUIDESet.new()\n\t# The new inputs that we will use for the action mappings.\n\tvar new_inputs:GUIDESet = GUIDESet.new()\n\t# The new action mappings that we will use from now on.\n\tvar new_action_mappings:Array[GUIDEActionMapping] = []\n\t# The new modifiers that we will use\n\tvar new_modifiers:GUIDESet = GUIDESet.new()\n\n\t# Step 0: walk over the new contexts and save over all inputs and modifiers that we\n\t# are going to keep. This is needed to ensure that we don't reset inputs and that if\n\t# new mappings don't create copies of existing inputs if they have a higher priority\n\t# than the existing ones (see https://github.com/godotneers/G.U.I.D.E/issues/94).\n\tfor context:GUIDEMappingContext in sorted_contexts:\n\t\tfor action_mapping:GUIDEActionMapping in context.mappings:\n\t\t\tfor existing_mapping:GUIDEActionMapping in _active_action_mappings:\n\t\t\t\tif _is_same_action_mapping(existing_mapping, action_mapping):\n\t\t\t\t\t# we will keep using this mapping, so we will make sure its inputs and modifiers\n\t\t\t\t\t# are kept and not duplicated. We don't add the action mapping to the new action mappings\n\t\t\t\t\t# yet, because the order of the action mappings is important and we will\n\t\t\t\t\t# add it later when we process the action mappings.\n\t\t\t\t\t\n\t\t\t\t\tfor input_mapping:GUIDEInputMapping in existing_mapping.input_mappings:\n\t\t\t\t\t\tif input_mapping.input != null:\n\t\t\t\t\t\t\tnew_inputs.add(input_mapping.input)\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor modifier:GUIDEModifier in input_mapping.modifiers:\n\t\t\t\t\t\t\tnew_modifiers.add(modifier)\n\n\n\t# Step 1: Collect all action mappings from the currently enabled contexts.\n\tfor context:GUIDEMappingContext in sorted_contexts:\n\t\tvar position:int = 0\n\t\tfor action_mapping:GUIDEActionMapping in context.mappings:\n\t\t\tposition += 1\n\t\t\tvar action := action_mapping.action\n\t\t\t\n\t\t\t# Mapping may be misconfigured, so we need to handle the case\n\t\t\t# that the action is missing.\n\t\t\tif action == null:\n\t\t\t\tpush_warning(\"Mapping at position %s in context %s has no action set. This mapping will be ignored.\" % [position, context.resource_path])\n\t\t\t\tcontinue\n\t\t\t\n\t\t\t# If the action was already configured in a higher priority context,\n\t\t\t# we'll try to merge inputs with other contexts then skip the rest.\n\t\t\tif processed_actions.has(action):\n\t\t\t\tfor mapping in new_action_mappings:\n\t\t\t\t\tif mapping.action != action_mapping.action:\n\t\t\t\t\t\tcontinue\n\n\t\t\t\t\t_parse_input_mappings(action_mapping, context, action, new_inputs, new_modifiers, mapping)\n\t\t\t\t# skip\n\t\t\t\tcontinue\n\t\t\t\t\n\t\t\tprocessed_actions.add(action)\n\t\t\t# Reset the hold threshold for this action since we're rebuilding it from scratch.\n\t\t\t# This ensures we don't carry over stale values from previous cache updates.\n\t\t\taction._trigger_hold_threshold = -1.0\n\n\t\t\t# If the action mapping is the same as one that is already active,\n\t\t\t# we use the existing one instead of creating a new one.\n\t\t\t# We do this to avoid losing state in the  triggers and modifiers when\n\t\t\t# switching contexts. See https://github.com/godotneers/G.U.I.D.E/issues/67\n\t\t\t# for details. In addition there is no need to create new objects\n\t\t\t# if we already have a functional one (though the comparison of the mappings\n\t\t\t# is likely more expensive than the creation of a new one).\n\t\t\tvar found_existing:bool = false\n\t\t\tfor existing_mapping:GUIDEActionMapping in _active_action_mappings:\n\t\t\t\tif _is_same_action_mapping(existing_mapping, action_mapping):\n\t\t\t\t\t# we found an existing mapping, so we can just use it\n\t\t\t\t\t# and we can skip the rest of the processing for this mapping.\n\t\t\t\t\tnew_action_mappings.append(existing_mapping)\n\t\t\t\t\tfound_existing = true\n\t\t\t\t\tbreak\n\t\t\t\t\t\n\t\t\tif found_existing:\n\t\t\t\t# we already have this action mapping, so we can skip it\n\t\t\t\tcontinue\n\t\t\t\n\t\t\t# We consolidate the inputs here, so we'll internally build a new\n\t\t\t# action mapping that uses consolidated inputs rather than the\n\t\t\t# original ones. This achieves multiple things:\n\t\t\t# - if two actions check for the same input, we only need to\n\t\t\t#   process the input once instead of twice.\n\t\t\t# - it allows us to prioritize input, if two actions check for  \n\t\t\t#   the same input. This way the first action can consume the\n\t\t\t#   input and not have it affect further actions.\n\t\t\t# - we make sure nobody shares triggers as they are stateful and\n\t\t\t#   should not be shared.\n\t\t\t\n\t\t\tvar effective_mapping := GUIDEActionMapping.new()\n\t\t\teffective_mapping.action = action\n\t\t\t_copy_meta(action_mapping, effective_mapping)\n\n\t\t\teffective_mapping = _parse_input_mappings(action_mapping, context, action, new_inputs, new_modifiers, effective_mapping)\n\t\t\t\n\t\t\t# if any binding remains, add the mapping to the list of active\n\t\t\t# action mappings\n\t\t\tif not effective_mapping.input_mappings.is_empty():\n\t\t\t\tnew_action_mappings.append(effective_mapping)\n\n\t# now we can clean up stuff, that we don't need anymore.\n\t# we start with the inputs that are no longer used.\n\tfor input:GUIDEInput in _active_inputs.values():\n\t\t# because we consolidated inputs, we can do an instance check rather than\n\t\t# a is_same_as check.\n\t\tif new_inputs.has(input):\n\t\t\tcontinue\n\n\t\t# this input is no longer used, so we can reset it\n\t\t# and notify it that it is no longer used.\n\t\tinput._reset()\n\t\tinput._end_usage()\n\t\tinput._state = null\n\t\t_mark_used(input, false)\n\t\t\n\t# and now the consolidated inputs are the new active inputs.\n\t_active_inputs = new_inputs\n\t# only modifiers that require physics processing are considered \"active\" modifiers\n\t_active_modifiers = new_modifiers.filter(func(it:GUIDEModifier) -> bool: return it._needs_physics_process())\n\t# only enable physics_processing if we actually have an active modifiers\n\tset_physics_process(not _active_modifiers.is_empty())\n\t\n\t# Now action mappings and their modifiers.\n\tfor mapping:GUIDEActionMapping in _active_action_mappings:\n\t\tif new_action_mappings.has(mapping):\n\t\t\t# this mapping is still active, so we can skip it\n\t\t\tcontinue\n\n\t\t# Cancel all actions that are going away, so they don't end up in a weird state.\n\t\tmatch mapping.action._last_state:\n\t\t\tGUIDEAction.GUIDEActionState.ONGOING:\n\t\t\t\tmapping.action._cancelled(Vector3.ZERO)\n\t\t\tGUIDEAction.GUIDEActionState.TRIGGERED:\n\t\t\t\tmapping.action._completed(Vector3.ZERO)\n\t\t\n\t\t# notify all modifiers they are no longer in use\n\t\tfor input_mapping in mapping.input_mappings:\n\t\t\tfor modifier in input_mapping.modifiers:\n\t\t\t\t# because modifiers can be shared, we need to check if the modifier\n\t\t\t\t# is still in use by any other action mapping that remains in use.\n\t\t\t\tif not new_modifiers.has(modifier):\n\t\t\t\t\tmodifier._end_usage()\n\t\t\t\t\t_mark_used(modifier, false)\n\t\n\t# and now we can assign the new action mappings\n\t_active_action_mappings = new_action_mappings\n\t\n\t# prepare the action input share lookup table\n\t_actions_sharing_input.clear()\n\tfor i:int in _active_action_mappings.size():\n\t\t\n\t\tvar mapping := _active_action_mappings[i]\n\t\t\n\t\tif mapping.action.block_lower_priority_actions:\n\t\t\t# first find out if the action uses any chorded actions and \n\t\t\t# collect all inputs that this action uses\n\t\t\tvar chorded_actions:GUIDESet = GUIDESet.new()\n\t\t\tvar inputs:GUIDESet = GUIDESet.new()\n\t\t\tvar blocked_actions:GUIDESet = GUIDESet.new()\n\t\t\tfor input_mapping:GUIDEInputMapping in mapping.input_mappings:\n\t\t\t\tif input_mapping.input != null:\n\t\t\t\t\tinputs.add(input_mapping.input)\t\t\n\t\t\t\t\t\t\n\t\t\t\tfor trigger:GUIDETrigger in input_mapping.triggers:\n\t\t\t\t\tif trigger is GUIDETriggerChordedAction and trigger.action != null:\n\t\t\t\t\t\tchorded_actions.add(trigger.action)\n\t\t\t\t\t\t\n\t\t\t# Now the action that has a chorded action (A) needs to make sure that\n\t\t\t# the chorded action it depends upon (B) is not blocked (otherwise A would \n\t\t\t# never trigger) and if that chorded action (B) in turn depends on chorded actions. So \n\t\t\t# if chorded actions build a chain, we need to keep the full\n\t\t\t# chain unblocked. In addition we need to add the inputs of all\n\t\t\t# these chorded actions to the list of blocked inputs.\n\t\t\tfor j:int in range(i+1, _active_action_mappings.size()):\n\t\t\t\tvar inner_mapping := _active_action_mappings[j]\n\t\t\t\t# this is a chorded action that is used by one other action\n\t\t\t\t# in the chain.\n\t\t\t\tif chorded_actions.has(inner_mapping.action):\n\t\t\t\t\tfor input_mapping:GUIDEInputMapping in inner_mapping.input_mappings:\n\t\t\t\t\t\t# put all of its inputs into the list of blocked inputs\n\t\t\t\t\t\tif input_mapping.input != null:\n\t\t\t\t\t\t\tinputs.add(input_mapping.input)\n\t\t\t\n\t\t\t\t\t\t# also if this mapping in turn again depends on a chorded\n\t\t\t\t\t\t# action, ad this one to the list of chorded actions\n\t\t\t\t\t\tfor trigger:GUIDETrigger in input_mapping.triggers:\n\t\t\t\t\t\t\tif trigger is GUIDETriggerChordedAction and trigger.action != null:\n\t\t\t\t\t\t\t\tchorded_actions.add(trigger.action)\n\t\t\t\n\t\t\t# now find lower priority actions that share input\n\t\t\tfor j:int in range(i+1, _active_action_mappings.size()):\n\t\t\t\tvar inner_mapping := _active_action_mappings[j]\n\t\t\t\tif chorded_actions.has(inner_mapping.action):\n\t\t\t\t\tcontinue\n\t\t\t\t\t\n\t\t\t\tfor input_mapping:GUIDEInputMapping in inner_mapping.input_mappings:\n\t\t\t\t\tif input_mapping.input == null:\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t\n\t\t\t\t\t# because we consolidated input, we can now do an == comparison\n\t\t\t\t\t# to find equal input.\n\t\t\t\t\tif inputs.has(input_mapping.input):\n\t\t\t\t\t\tblocked_actions.add(inner_mapping.action)\n\t\t\t\t\t\t# we can continue to the next action\n\t\t\t\t\t\tbreak \n\t\t\t\t\t\t\t\t\t\t\n\t\t\tif not blocked_actions.is_empty():\n\t\t\t\t_actions_sharing_input[mapping.action] = blocked_actions.values()\n\n\t# collect which inputs we need to reset per frame\n\t_reset_node._inputs_to_reset.clear()\n\tfor input:GUIDEInput in _active_inputs.values():\n\t\tif input._needs_reset():\n\t\t\t_reset_node._inputs_to_reset.append(input)\n\t\t\n\t# run a round of _process so we can be sure our actions are \n\t# up-to-date\n\t_process(0.0)\n\t\n\t# unlock\n\t_locked = false\n\t\n\t# and notify interested parties that the input mappings have changed\n\tinput_mappings_changed.emit()\n\n## Helper function which determines whether two action mappings are the same.\n## They are the same if they have the same action, the same input mappings\n## the same modifiers and the same triggers. Same doesn't necessarily mean\n## they are the same instance, but rather that they are equivalent in terms of\n## their configuration.\nstatic func _is_same_action_mapping(a:GUIDEActionMapping, b:GUIDEActionMapping) -> bool:\n\t# If its the same instance, we can just return true.\n\tif a == b:\n\t\treturn true\n\t\t\n\t# If they don't have the same action, they cannot be the same.\n\tif a.action != b.action:\n\t\treturn false\n\t\n\t# If they don't have the same number of input mappings, they cannot be the same.\n\tif a.input_mappings.size() != b.input_mappings.size():\n\t\treturn false\n\n\t# Now check all input mappings.\n\tfor i:int in range(a.input_mappings.size()):\n\t\tvar input_mapping_a:GUIDEInputMapping = a.input_mappings[i]\n\t\tvar input_mapping_b:GUIDEInputMapping = b.input_mappings[i]\n\t\t\n\t\tvar input_a:GUIDEInput = input_mapping_a.input\n\t\tvar input_b:GUIDEInput = input_mapping_b.input\n\t\t\n\t\tif input_a != null and input_b != null:\n\t\t\t# If the inputs are not the same, they cannot be the same.\n\t\t\tif not input_mapping_a.input.is_same_as(input_mapping_b.input):\n\t\t\t\treturn false\n\t\telif input_a != input_b:\n\t\t\t# If one input is null and the other is not, they cannot be the same.\n\t\t\treturn false\n\t\t\n\t\t# If the modifiers are not the same, they cannot be the same.\n\t\tif input_mapping_a.modifiers.size() != input_mapping_b.modifiers.size():\n\t\t\treturn false\n\t\t\n\t\tfor j:int in range(input_mapping_a.modifiers.size()):\n\t\t\tvar modifier_a:GUIDEModifier = input_mapping_a.modifiers[j]\n\t\t\tvar modifier_b:GUIDEModifier = input_mapping_b.modifiers[j]\n\t\t\t\n\t\t\tif modifier_a != null and modifier_b != null:\n\t\t\t\t# If the modifiers are not the same, they cannot be the same.\n\t\t\t\tif not modifier_a.is_same_as(modifier_b):\n\t\t\t\t\treturn false\n\t\t\telif modifier_a != modifier_b:\n\t\t\t\t# If one modifier is null and the other is not, they cannot be the same.\n\t\t\t\treturn false\n\t\t\n\t\t# If the triggers are not the same, they cannot be the same.\n\t\tif input_mapping_a.triggers.size() != input_mapping_b.triggers.size():\n\t\t\treturn false\n\t\t\n\t\tfor j:int in range(input_mapping_a.triggers.size()):\n\t\t\tvar trigger_a:GUIDETrigger = input_mapping_a.triggers[j]\n\t\t\tvar trigger_b:GUIDETrigger = input_mapping_b.triggers[j]\n\t\t\t\n\t\t\tif trigger_a != null and trigger_b != null:\n\t\t\t\t# If the triggers are not the same, they cannot be the same.\n\t\t\t\tif not trigger_a.is_same_as(trigger_b):\n\t\t\t\t\treturn false\n\t\t\telif trigger_a != trigger_b:\n\t\t\t\t# If one trigger is null and the other is not, they cannot be the same.\n\t\t\t\treturn false\n\t\t\n\treturn true\n\nstatic func _mark_used(object: Object, value:bool) -> void:\n\tif value:\n\t\tobject.set_meta(\"__guide_in_use\", value)\n\telse:\n\t\tobject.remove_meta(\"__guide_in_use\")\n\nstatic func _is_used(object: Object) -> bool:\n\treturn object.has_meta(\"__guide_in_use\")\n\t\n\nstatic func _copy_meta(source:Object, target:Object) -> void:\n\tvar keys:Array[StringName] = source.get_meta_list()\n\tfor key:StringName in keys:\n\t\ttarget.set_meta(key, source.get_meta(key))\n"
  },
  {
    "path": "addons/guide/guide.gd.uid",
    "content": "uid://p2jwrgjcbexr\n"
  },
  {
    "path": "addons/guide/guide_action.gd",
    "content": "@tool\n@icon(\"res://addons/guide/guide_action.svg\")\nclass_name GUIDEAction\nextends Resource\n\nenum GUIDEActionValueType {\n\tBOOL = 0,\n\tAXIS_1D = 1,\n\tAXIS_2D = 2,\n\tAXIS_3D = 3\n}\n\nenum GUIDEActionState {\n\tTRIGGERED,\n\tONGOING,\n\tCOMPLETED\n}\n\n## The name of this action. Required when this action should be used as\n## Godot action. Also displayed in the debugger.\n@export var name:StringName:\n\tset(value):\n\t\tif name == value:\n\t\t\treturn\n\t\tname = value\n\t\temit_changed()\n\t\n\n## The action value type.\n@export var action_value_type: GUIDEActionValueType = GUIDEActionValueType.BOOL:\n\tset(value):\n\t\tif action_value_type == value:\n\t\t\treturn\n\t\taction_value_type = value\n\t\temit_changed()\n\t\t\n## If this action triggers, lower-priority actions cannot trigger \n## if they share input with this action unless these actions are\n## chorded with this action.\t\t\n@export var block_lower_priority_actions:bool = true:\n\tset(value):\n\t\tif block_lower_priority_actions == value:\n\t\t\treturn\n\t\tblock_lower_priority_actions = value\n\t\temit_changed()\t\n\n\n@export_category(\"Godot Actions\")\n## If true, then this action will be emitted into Godot's \n## built-in action system. This can be helpful to interact with\n## code using this system, like Godot's UI system. Actions\n## will be emitted on trigger and completion (e.g. button down\n## and button up).\n@export var emit_as_godot_actions:bool = false:\n\tset(value):\n\t\tif emit_as_godot_actions == value:\n\t\t\treturn\n\t\temit_as_godot_actions = value\n\t\temit_changed()\n\t\t\n\t\t\n@export_category(\"Action Remapping\")\n\n## If true, players can remap this action. To be remappable, make sure\n## that a name and the action type are properly set.\n@export var is_remappable:bool:\n\tset(value):\n\t\tif is_remappable == value:\n\t\t\treturn\n\t\tis_remappable = value\n\t\temit_changed()\n\t\t\n## The display name of the action shown to the player.\n@export var display_name:String:\n\tset(value):\n\t\tif display_name == value:\n\t\t\treturn\n\t\tdisplay_name = value\n\t\temit_changed()\n\n## The display category of the action shown to the player.\n@export var display_category:String:\n\tset(value):\n\t\tif display_category == value:\n\t\t\treturn\n\t\tdisplay_category = value\n\t\temit_changed()\n\t\t\n## Emitted every frame while the action is triggered.\nsignal triggered()\n\n## Emitted the first frame that the action is triggered.\nsignal just_triggered()\n\n## Emitted when the action started evaluating.\nsignal started()\n\n## Emitted every frame while the action is still evaluating.\nsignal ongoing()\n\n## Emitted when the action finished evaluating.\nsignal completed()\n\n## Emitted when the action was cancelled.\nsignal cancelled()\n\nvar _last_state:GUIDEActionState = GUIDEActionState.COMPLETED\n\nvar _value_bool:bool\n## Returns the value of this action as bool.\nvar value_bool:bool:\n\tget: return _value_bool\n\n## Returns the value of this action as float.\nvar value_axis_1d:float:\n\tget: return _value.x\n\t\t\nvar _value_axis_2d:Vector2 = Vector2.ZERO\n## Returns the value of this action as Vector2.\nvar value_axis_2d:Vector2:\n\tget: return _value_axis_2d\n\nvar _value:Vector3 = Vector3.ZERO\n## Returns the value of this action as Vector3.\nvar value_axis_3d:Vector3:\n\tget: return _value\n\t\n\nvar _elapsed_seconds:float\n## The amount of seconds elapsed since the action started evaluating.\nvar elapsed_seconds:float:\n\tget: return _elapsed_seconds\n\nvar _elapsed_ratio:float\n## The ratio of the elapsed time to the hold time. This is a percentage\n## of the hold time that has passed. If the action has no hold time, this will\n## be 0 when the action is not triggered and 1 when the action is triggered.\n## Otherwise, this will be a value between 0 and 1.\nvar elapsed_ratio:float:\n\tget: return _elapsed_ratio\n\nvar _triggered_seconds:float\n## The amount of seconds elapsed since the action triggered.\nvar triggered_seconds:float:\n\tget: return _triggered_seconds\n\n\n## This is a hint for how long the input must remain actuated (in seconds) before the action triggers.\n## It depends on the mapping in which this action is used. If the mapping has no hold trigger it will be -1.\n## In general, you should not access this variable directly, but rather the `elapsed_ratio` property of the action\n## which is a percentage of the hold time that has passed.\nvar _trigger_hold_threshold:float = -1.0\n\nfunc _triggered(value:Vector3, delta:float) -> void:\n\t_triggered_seconds += delta\n\t_elapsed_ratio = 1.0\n\t_update_value(value)\n\tif _last_state != GUIDEActionState.TRIGGERED:\n\t\tjust_triggered.emit()\n\t_last_state = GUIDEActionState.TRIGGERED\n\ttriggered.emit()\n\t_emit_godot_action_maybe(true)\n\t\t\nfunc _started(value:Vector3) -> void:\n\t_elapsed_ratio = 0.0\n\t_update_value(value)\n\t_last_state = GUIDEActionState.ONGOING\n\tstarted.emit()\n\tongoing.emit()\n\nfunc _ongoing(value:Vector3, delta:float) -> void:\n\t_elapsed_seconds += delta\n\tif _trigger_hold_threshold > 0:\n\t\t_elapsed_ratio = _elapsed_seconds / _trigger_hold_threshold\n\t_update_value(value)\n\tvar was_triggered:bool = _last_state == GUIDEActionState.TRIGGERED\n\t_last_state = GUIDEActionState.ONGOING\n\tongoing.emit()\n\t# if the action reverts from triggered to ongoing, this counts as \n\t# releasing the action for the godot action system.\n\tif was_triggered:\n\t\t_emit_godot_action_maybe(false)\n\t\n\nfunc _cancelled(value:Vector3) -> void:\n\t_elapsed_seconds = 0\n\t_elapsed_ratio = 0\n\t_update_value(value)\n\t_last_state = GUIDEActionState.COMPLETED\n\tcancelled.emit()\n\tcompleted.emit()\n\nfunc _completed(value:Vector3) -> void:\n\t_elapsed_seconds = 0\n\t_elapsed_ratio = 0\n\t_triggered_seconds = 0\n\t_update_value(value)\n\t_last_state = GUIDEActionState.COMPLETED\n\tcompleted.emit()\n\t_emit_godot_action_maybe(false)\n\t\t\nfunc _emit_godot_action_maybe(pressed:bool) -> void:\n\tif not emit_as_godot_actions:\n\t\treturn\n\t\t\n\tif name.is_empty():\n\t\tpush_error(\"Cannot emit action into Godot's system because name is empty.\")\n\t\treturn\n\t\t\n\tvar godot_action := InputEventAction.new()\n\tgodot_action.action = name\n\tgodot_action.strength = _value.x\n\tgodot_action.pressed = pressed\n\tInput.parse_input_event(godot_action)\n\nfunc _update_value(value:Vector3):\n\tmatch action_value_type:\n\t\tGUIDEActionValueType.BOOL, GUIDEActionValueType.AXIS_1D:\n\t\t\t_value_bool = abs(value.x) > 0\n\t\t\t_value_axis_2d = Vector2(abs(value.x), 0)\n\t\t\t_value = Vector3(value.x, 0, 0)\n\t\tGUIDEActionValueType.AXIS_2D:\n\t\t\t_value_bool = abs(value.x) > 0\n\t\t\t_value_axis_2d = Vector2(value.x, value.y)\n\t\t\t_value = Vector3(value.x, value.y, 0)\n\t\tGUIDEActionValueType.AXIS_3D:\n\t\t\t_value_bool = abs(value.x) > 0\n\t\t\t_value_axis_2d = Vector2(value.x, value.y)\n\t\t\t_value = value\n\n## Returns whether the action is currently triggered. Can be used for a \n## polling style input.\nfunc is_triggered() -> bool:\n\treturn _last_state == GUIDEActionState.TRIGGERED\n\n\t\n## Returns whether the action is currently completed. Can be used for a \n## polling style input.\nfunc is_completed() -> bool:\n\treturn _last_state == GUIDEActionState.COMPLETED\n\n\n## Returns whether the action is currently completed. Can be used for a \n## polling style input.\nfunc is_ongoing() -> bool:\n\treturn _last_state == GUIDEActionState.ONGOING\n\n\nfunc _editor_name() -> String:\n\t# Try to give the most user friendly name\n\tif display_name != \"\":\n\t\treturn display_name\n\t\t\n\tif name != \"\":\n\t\treturn name\n\t\t\n\treturn resource_path.get_file().replace(\".tres\", \"\")\n\n\n"
  },
  {
    "path": "addons/guide/guide_action.gd.uid",
    "content": "uid://cluhc11vixkf1\n"
  },
  {
    "path": "addons/guide/guide_action.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bei7cw115tks0\"\npath=\"res://.godot/imported/guide_action.svg-4d1dfb47183d95c4796078798ce2d0ab.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/guide_action.svg\"\ndest_files=[\"res://.godot/imported/guide_action.svg-4d1dfb47183d95c4796078798ce2d0ab.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=0.5\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/guide_action_mapping.gd",
    "content": "@icon(\"res://addons/guide/guide_internal.svg\")\n@tool\n## An action to input mapping\nclass_name GUIDEActionMapping\nextends Resource\n\n## The action to be mapped\n@export var action:GUIDEAction:\n\tset(value):\n\t\tif value == action:\n\t\t\treturn\n\t\taction = value\n\t\temit_changed()\n\n## A set of input mappings that can trigger the action\n@export var input_mappings:Array[GUIDEInputMapping] = []:\n\tset(value):\n\t\tif value == input_mappings:\n\t\t\treturn\n\t\tinput_mappings = value\n\t\temit_changed()\t\t\n"
  },
  {
    "path": "addons/guide/guide_action_mapping.gd.uid",
    "content": "uid://cpplm41b5bt6m\n"
  },
  {
    "path": "addons/guide/guide_input_mapping.gd",
    "content": "@icon(\"res://addons/guide/guide_internal.svg\")\n@tool\n## A mapping from actuated input to a trigger result\nclass_name GUIDEInputMapping\nextends Resource\n\n## Whether the remapping configuration in this input mapping\n## should override the configuration of the bound action. Enable\n## this, to give a key a custom name or category for remapping.\n@export var override_action_settings:bool = false:\n\tset(value):\n\t\tif override_action_settings == value:\n\t\t\treturn\n\t\toverride_action_settings = value\n\t\temit_changed()\n\n## If true, players can remap this input mapping. Note that the \n## action to which this input is bound also needs to be remappable\n## for this setting to have an effect.\n@export var is_remappable:bool = false:\n\tset(value):\n\t\tif is_remappable == value:\n\t\t\treturn\n\t\tis_remappable = value\n\t\temit_changed()\n\t\t\n## The display name of the input mapping shown to the player. If empty,\n## the display name of the action is used.\n@export var display_name:String = \"\":\n\tset(value):\n\t\tif display_name == value:\n\t\t\treturn\n\t\tdisplay_name = value\n\t\temit_changed()\n\n## The display category of the input mapping. If empty, the display name of the\n## action is used.\n@export var display_category:String = \"\":\n\tset(value):\n\t\tif display_category == value:\n\t\t\treturn\n\t\tdisplay_category = value\n\t\temit_changed()\n\t\t\n\n@export_group(\"Mappings\")\n## The input to be actuated\n@export var input:GUIDEInput:\n\tset(value):\n\t\tif value == input:\n\t\t\treturn\n\t\tinput = value\n\t\temit_changed()\n\n\n## A list of modifiers that preprocess the actuated input before\n## it is fed to the triggers.\n@export var modifiers:Array[GUIDEModifier] = []:\n\tset(value):\n\t\tif value == modifiers:\n\t\t\treturn\n\t\tmodifiers = value\n\t\temit_changed()\n\n\n## A list of triggers that could trigger the mapped action.\n@export var triggers:Array[GUIDETrigger] = []:\n\tset(value):\n\t\tif value == triggers:\n\t\t\treturn\n\t\ttriggers = value\n\t\temit_changed()\n\n## Hint for how long the input must remain actuated (in seconds) before the mapping triggers.\n## If the mapping has no hold trigger it will be -1. If it has multiple hold triggers\n## the shortest hold time will be used.\nvar _trigger_hold_threshold:float = -1.0\n\nvar _state:GUIDETrigger.GUIDETriggerState = GUIDETrigger.GUIDETriggerState.NONE\nvar _value:Vector3 = Vector3.ZERO\n\nvar _trigger_list:Array[GUIDETrigger] = []\nvar _implicit_count:int = 0\nvar _explicit_count:int = 0\n\n## Called when the mapping is started to be used by GUIDE. Calculates \n## the number of implicit and explicit triggers so we don't need to do this\n## per frame. Also creates a default trigger when none is set.\n## finally initializes the _last_value of all triggers to the current\n## state of the input.\nfunc _initialize(value_type:GUIDEAction.GUIDEActionValueType) -> void :\n\t_trigger_list.clear()\n\t\n\t_implicit_count = 0\n\t_explicit_count = 0\n\t_trigger_hold_threshold = -1.0\n\t\n\tif triggers.is_empty():\n\t\t# make a default trigger and use that\n\t\tvar default_trigger := GUIDETriggerDown.new()\n\t\tdefault_trigger.actuation_threshold = 0\n\t\t_explicit_count = 1\n\t\t_trigger_list.append(default_trigger)\n\t\treturn\n\t\t\n\t# Collect the current input value\n\tvar input_value:Vector3 = input._value if input != null else Vector3.ZERO\n\t\n\t# Run it through all modifiers\n\tfor modifier:GUIDEModifier in modifiers:\n\t\tinput_value = modifier._modify_input(input_value, 0, value_type)\t\t\n\t\n\tfor trigger in triggers:\n\t\tmatch trigger._get_trigger_type():\n\t\t\tGUIDETrigger.GUIDETriggerType.EXPLICIT:\n\t\t\t\t_explicit_count += 1\n\t\t\tGUIDETrigger.GUIDETriggerType.IMPLICIT:\n\t\t\t\t_implicit_count += 1\n\t\t_trigger_list.append(trigger)\n\t\t\n\t\t# collect the hold threshold for hinting the UI about how long\n\t\t# the input must be held down. This is only relevant for hold triggers\n\t\tif trigger is GUIDETriggerHold:\n\t\t\tif _trigger_hold_threshold == -1:\n\t\t\t\t_trigger_hold_threshold = trigger.hold_treshold\n\t\t\telse:\n\t\t\t\t_trigger_hold_threshold = min(_trigger_hold_threshold, trigger.hold_treshold)\n\t\t\n\t\t# initialize the last value, so that e.g. the \"pressed\" trigger\n\t\t# will not immediately trigger when the key was already \n\t\t# pressed when the trigger came to life.\n\t\ttrigger._last_value = input_value\n\t\t\n\nfunc _update_state(delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> void:\n\t# Collect the current input value\n\tvar input_value:Vector3 = input._value if input != null else Vector3.ZERO\n\t\n\t# Run it through all modifiers\n\tfor modifier:GUIDEModifier in modifiers:\n\t\tinput_value = modifier._modify_input(input_value, delta, value_type)\n\t\t\n\t_value = input_value\n\t\n\tvar triggered_implicits:int = 0\n\tvar triggered_explicits:int = 0\n\tvar triggered_blocked:int = 0\n\t\n\t# Run over all triggers\n\tvar result:int = GUIDETrigger.GUIDETriggerState.NONE\n\tfor trigger:GUIDETrigger in _trigger_list:\n\t\tvar trigger_result:GUIDETrigger.GUIDETriggerState = trigger._update_state(_value, delta, value_type)\n\t\ttrigger._last_value = _value\n\t\t\n\t\tvar trigger_type := trigger._get_trigger_type()\n\t\tif trigger_result == GUIDETrigger.GUIDETriggerState.TRIGGERED:\n\t\t\tmatch trigger_type:\n\t\t\t\tGUIDETrigger.GUIDETriggerType.EXPLICIT:\n\t\t\t\t\ttriggered_explicits += 1\n\t\t\t\tGUIDETrigger.GUIDETriggerType.IMPLICIT:\n\t\t\t\t\ttriggered_implicits += 1\n\t\t\t\tGUIDETrigger.GUIDETriggerType.BLOCKING:\n\t\t\t\t\ttriggered_blocked += 1\n\t\t\t\n\t\t# we only care about the nuances of explicit triggers. implicits and blocking\n\t\t# can only really return yes or no, so they have no nuance\t\t\n\t\tif trigger_type == GUIDETrigger.GUIDETriggerType.EXPLICIT: \n\t\t\t# Higher value results take precedence over lower value results\n\t\t\tresult = max(result, trigger_result)\n\t\n\t# final collection\n\tif triggered_blocked > 0:\n\t\t# some blocker triggered which means that this cannot succeed\n\t\t_state = GUIDETrigger.GUIDETriggerState.NONE\n\t\treturn\n\t\n\tif triggered_implicits < _implicit_count:\n\t\t# not all implicits triggered, which also fails this binding\n\t\t_state = GUIDETrigger.GUIDETriggerState.NONE\n\t\treturn\n\t\n\tif _explicit_count == 0 and _implicit_count > 0:\n\t\t# if no explicits exist, its enough when all implicits trigger\n\t\t_state = GUIDETrigger.GUIDETriggerState.TRIGGERED\n\t\treturn\n\t\t\n\t# return the best result\n\t_state = result\n\n\t\n"
  },
  {
    "path": "addons/guide/guide_input_mapping.gd.uid",
    "content": "uid://mtx1unc2aqn7\n"
  },
  {
    "path": "addons/guide/guide_input_tracker.gd",
    "content": "## Tracker that tracks input for a window and injects it into GUIDE.\n## Will automatically keep track of sub-windows.\nextends Node\n\n## Instruments a sub-window so it forwards input events to GUIDE.\nstatic func _instrument(viewport:Viewport):\n\tif viewport.has_meta(\"_x_guide_instrumented\"):\n\t\treturn\n\t\n\tvar tracker = preload(\"guide_input_tracker.gd\").new()\n\ttracker.process_mode = Node.PROCESS_MODE_ALWAYS\n\tviewport.add_child(tracker, false, Node.INTERNAL_MODE_BACK)\n\tviewport.gui_focus_changed.connect(tracker._control_focused)\n\tviewport.set_meta(\"_x_guide_instrumented\", true)\n\t\n## Catches unhandled input and forwards it to GUIDE\nfunc _unhandled_input(event:InputEvent):\n\tGUIDE.inject_input(event)\n\n## Some ... creative code ... to catch events from popup windows\n## that are spawned by Godot's control nodes.\nfunc _control_focused(control:Control):\n\tif control is OptionButton or control is ColorPickerButton \\\n\t\t\tor control is MenuButton or control is TabContainer:\n\t\tvar popup:Viewport = control.get_popup()\n\t\tif is_instance_valid(popup):\n\t\t\t_instrument(popup)\t\n\t\n\t\n"
  },
  {
    "path": "addons/guide/guide_input_tracker.gd.uid",
    "content": "uid://d2elu0augsms\n"
  },
  {
    "path": "addons/guide/guide_internal.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://ddkj7kntb4fit\"\npath=\"res://.godot/imported/guide_internal.svg-560a143a1e289215e72d8844f5173844.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/guide_internal.svg\"\ndest_files=[\"res://.godot/imported/guide_internal.svg-560a143a1e289215e72d8844f5173844.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=0.5\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/guide_mapping_context.gd",
    "content": "@tool\n@icon(\"res://addons/guide/guide_mapping_context.svg\")\nclass_name GUIDEMappingContext\nextends Resource\n\n## Emitted when this mapping context is enabled.\nsignal enabled()\n\n## Emitted when this mapping context is disabled.\nsignal disabled()\n\nconst GUIDESet = preload(\"guide_set.gd\")\n\n## The display name for this mapping context during action remapping \n@export var display_name:String:\n\tset(value):\n\t\tif value == display_name:\n\t\t\treturn\n\t\tdisplay_name = value\n\t\temit_changed()\n\n## The mappings. Do yourself a favour and use the G.U.I.D.E panel\n## to edit these.\n@export var mappings:Array[GUIDEActionMapping] = []:\n\tset(value):\n\t\tif value == mappings:\n\t\t\treturn\n\t\tmappings = value\n\t\temit_changed()\n\n\nfunc _editor_name() -> String:\n\tif display_name.is_empty():\n\t\treturn resource_path.get_file()\n\telse:\n\t\treturn display_name\n"
  },
  {
    "path": "addons/guide/guide_mapping_context.gd.uid",
    "content": "uid://dsa1dnifd6w32\n"
  },
  {
    "path": "addons/guide/guide_mapping_context.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bcwpqc8016n7b\"\npath=\"res://.godot/imported/guide_mapping_context.svg-025f10fbbdb2bb11a96754ab9b725bea.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/guide_mapping_context.svg\"\ndest_files=[\"res://.godot/imported/guide_mapping_context.svg-025f10fbbdb2bb11a96754ab9b725bea.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=0.5\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/guide_reset.gd",
    "content": "extends Node\n\n\nvar _inputs_to_reset:Array[GUIDEInput] = []\n\nfunc _enter_tree() -> void:\n\t# this should run at the end of the frame, so we put in a low priority (= high number)\n\tprocess_priority = 10000000\n\n# Called every frame. 'delta' is the elapsed time since the previous frame.\nfunc _process(delta: float) -> void:\n\tfor input:GUIDEInput in _inputs_to_reset:\n\t\tinput._reset()\n\t\t\n\tGUIDE._input_state._reset()\n"
  },
  {
    "path": "addons/guide/guide_reset.gd.uid",
    "content": "uid://cgr8gmw4k8j5x\n"
  },
  {
    "path": "addons/guide/guide_set.gd",
    "content": "## Helper class for modelling sets\n\nconst GUIDESet = preload(\"guide_set.gd\")\n\nvar _values: Dictionary = {}\n\n\n## Adds the given value to the set.\n## If the value is already in the set, it will not be added again.\nfunc add(value: Variant) -> void:\n\t_values[value] = value\n\n\n## Adds all values in the given array to the set.\n## If a value is already in the set, it will not be added again.\nfunc add_all(values: Array) -> void:\n\tfor value in values:\n\t\t_values[value] = value\n\n\n## Removes the given value from the set.\nfunc remove(value: Variant) -> void:\n\t_values.erase(value)\n\n\n## Removes all values from the set.\nfunc clear() -> void:\n\t_values.clear()\n\n\n## Returns true if the set is empty, false otherwise.\nfunc is_empty() -> bool:\n\treturn _values.is_empty()\n\t\n\n## Returns a new set containing only the values for which the given predicate returns true.\n## The predicate should take a single argument and return a boolean.\nfunc filter(predicate:Callable) -> GUIDESet:\n\tvar result = GUIDESet.new()\n\tfor key in _values.keys():\n\t\tif predicate.call(key):\n\t\t\tresult.add(key)\n\treturn result\n\n## Returns the first item in the set and removes it from the set.\n## If the set is empty, returns null.\nfunc pull() -> Variant:\n\tif is_empty():\n\t\treturn null\n\t\n\tvar key = _values.keys()[0]\n\tremove(key)\n\treturn key\n\n\n## Checks whether the set contains the given value.\nfunc has(value: Variant) -> bool:\n\treturn _values.has(value)\n\n\n## Returns the first item for which the given matcher function returns\n## a true value.\nfunc first_match(matcher: Callable) -> Variant:\n\tfor key in _values.keys():\n\t\tif matcher.call(key):\n\t\t\treturn key\n\treturn null\n\n\n## Assigns all values in the set to the given array.\nfunc assign_to(values: Array) -> void:\n\tvalues.assign(_values.keys())\n\n\n## Returns an array of all values in the set.\nfunc values() -> Array:\n\treturn _values.keys()\n\n\t\n## Returns the number of items in the set.\nfunc size() -> int:\n\treturn _values.size()\n"
  },
  {
    "path": "addons/guide/guide_set.gd.uid",
    "content": "uid://ctvyc3kv17qad\n"
  },
  {
    "path": "addons/guide/inputs/guide_input.gd",
    "content": "@tool\n@icon(\"res://addons/guide/inputs/guide_input.svg\")\n## A class representing some actuated input.\nclass_name GUIDEInput\nextends Resource\n\nenum DeviceType {\n\t## The input originates from no device (e.g. virtual inputs).\n\tNONE = 0,\n\t## The input originates from a keyboard.\n\tKEYBOARD = 1,\n\t## The input originates from a mouse.\n\tMOUSE = 2,\n\t## The input originates from a joystick / gamepad.\n\tJOY = 4,\n\t## The input originates from a touch device.\n\tTOUCH = 8,\n}\n\n## The type of device from which this input originates. Note that this can\n## also be a combination of devices (e.g. for the any input).\nvar device_type:DeviceType:\n\tget: return _device_type()\n\n## The current valueo f this input. Depending on the input type only parts of the \n## returned vector may be relevant.\nvar _value:Vector3 = Vector3.ZERO\n\n## The current input state. This will be set by GUIDE when the input is used.\nvar _state:GUIDEInputState = null\n\n## Whether this input needs a reset per frame. _input is only called when\n## there is input happening, but some GUIDE inputs may need to be reset\n## in the absence of input.\nfunc _needs_reset() -> bool:\n\treturn false\n\t\n## Resets the input value to the default value. Is called once per frame if\n## _needs_reset returns true.\nfunc _reset() -> void:\n\t_value = Vector3.ZERO\n\n## Returns whether this input is the same input as the other input.\nfunc is_same_as(other:GUIDEInput) -> bool:\n\treturn false\n\t\n## Called when the input is started to be used by GUIDE. Can be used to perform\n## initializations. The state object can be used to subscribe to input events\n## and to get the current input state.\nfunc _begin_usage() -> void :\n\tpass\n\t\n## Called, when the input is no longer used by GUIDE. Can be used to perform\n## cleanup.\nfunc _end_usage() -> void:\n\tpass\n\t\n## The name of this input as it should be shown in the editor.\t\nfunc _editor_name() -> String:\n\treturn \"\"\n\t\n## The description of this input as it should be shown in the editor.\t\nfunc _editor_description() -> String:\n\treturn \"\"\n\t\n## The native value type of this input (e.g. which kind of value will the \n## input produce).\nfunc _native_value_type() -> GUIDEAction.GUIDEActionValueType:\n\treturn -1\n\n## The device type from which this input originates.\nfunc _device_type() -> DeviceType:\n\treturn DeviceType.NONE\n"
  },
  {
    "path": "addons/guide/inputs/guide_input.gd.uid",
    "content": "uid://ccvqqvfooyvn0\n"
  },
  {
    "path": "addons/guide/inputs/guide_input.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://oku7f5t0ox3r\"\npath=\"res://.godot/imported/guide_input.svg-d7e8ae255db039e6a02cccc3f844cc0e.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/inputs/guide_input.svg\"\ndest_files=[\"res://.godot/imported/guide_input.svg-d7e8ae255db039e6a02cccc3f844cc0e.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=0.5\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_action.gd",
    "content": "## An input that mirrors the action's value while the action is triggered.\n@tool\nclass_name GUIDEInputAction\nextends GUIDEInput\n\n## The action that this input should mirror. This is live tracked, so any change in\n## the action will update the input.\n@export var action:GUIDEAction:\n\tset(value):\n\t\tif value == action:\n\t\t\treturn\n\t\taction = value\n\t\temit_changed()\t\n\nfunc _begin_usage() -> void:\n\tif is_instance_valid(action):\n\t\taction.triggered.connect(_on)\n\t\taction.completed.connect(_off)\n\t\taction.ongoing.connect(_off)\n\t\tif action.is_triggered():\n\t\t\t_on()\n\t\t\treturn\n\t# not triggered or no action.\n\t_off()\n\t\n\t\nfunc _end_usage() -> void:\n\tif is_instance_valid(action):\n\t\taction.triggered.disconnect(_on)\n\t\taction.completed.disconnect(_off)\n\t\taction.ongoing.disconnect(_off)\n\n\nfunc _on() -> void:\n\t# on is only called when the action is actually existing, so this is\n\t# always not-null here\n\t_value = action.value_axis_3d\n\t\nfunc _off() -> void:\n\t_value = Vector3.ZERO\t\n\t\n\t\nfunc is_same_as(other:GUIDEInput) -> bool:\n\treturn other is GUIDEInputAction and other.action == action\n\n\nfunc _to_string() -> String:\n\treturn \"(GUIDEInputAction: \" + str(action) + \")\"\n\nfunc _editor_name() -> String:\n\treturn \"Action\"\n\t\n\t\nfunc _editor_description() -> String:\n\treturn \"An input that mirrors the action's value while the action is triggered.\"\n\t\n\nfunc _native_value_type() -> GUIDEAction.GUIDEActionValueType:\n\treturn GUIDEAction.GUIDEActionValueType.AXIS_3D\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_action.gd.uid",
    "content": "uid://mc0mxjvhanrx\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_any.gd",
    "content": "## Input that triggers if any input from the given device class\n## is given.\n@tool\nclass_name GUIDEInputAny\nextends GUIDEInput\n\n\n## Should input from mouse buttons be considered? Deprecated, use\n## mouse_buttons instead.\n## @deprecated \nvar mouse:bool:\n\tget: return mouse_buttons\n\tset(value): mouse_buttons = value\n\n## Should input from joy buttons be considered. Deprecated, use\n## joy_buttons instead.\n## @deprecated\nvar joy:bool:\n\tget: return joy_buttons\n\tset(value): joy_buttons = value\n\n## Should input from mouse buttons be considered?\n@export var mouse_buttons:bool = false\n\t\t\n## Should input from mouse movement be considered?\n@export var mouse_movement:bool = false\n\n## Minimum movement distance of the mouse before it is considered\n## moving.\n@export var minimum_mouse_movement_distance:float = 1.0\n\n## Should input from gamepad/joystick buttons be considered?\n@export var joy_buttons:bool = false\n\n## Should input from gamepad/joystick axes be considered?\n@export var joy_axes:bool = false \n\n## Minimum strength of a single joy axis actuation before it is considered\n## as actuated.\n@export var minimum_joy_axis_actuation_strength:float = 0.2\n\n## Should input from the keyboard be considered?\n@export var keyboard:bool = false\n\n## Should input from touch be considered?\n@export var touch:bool = false\n\n\nfunc _needs_reset() -> bool:\n\t# Needs reset because we cannot detect the absence of input.\n\treturn true\n\nfunc _reset() -> void:\n\t_value = Vector3.ZERO\n\t_refresh()\n\nfunc _begin_usage() -> void:\n\t# subscribe to relevant input events\n\tif mouse_movement:\n\t\t_state.mouse_position_changed.connect(_refresh)\n\tif mouse_buttons:\n\t\t_state.mouse_button_state_changed.connect(_refresh)\n\tif keyboard:\n\t\t_state.keyboard_state_changed.connect(_refresh)\n\tif joy_buttons:\n\t\t_state.joy_button_state_changed.connect(_refresh)\n\tif joy_axes:\n\t\t_state.joy_axis_state_changed.connect(_refresh)\n\tif touch:\n\t\t_state.touch_state_changed.connect(_refresh)\n\t_state.application_focus_lost.connect(_on_application_focus_lost)\n\t_refresh()\n\nfunc _end_usage() -> void:\n\t# unsubscribe from input events\n\tif mouse_movement:\n\t\t_state.mouse_position_changed.disconnect(_refresh)\n\tif mouse_buttons:\n\t\t_state.mouse_button_state_changed.disconnect(_refresh)\n\tif keyboard:\n\t\t_state.keyboard_state_changed.disconnect(_refresh)\n\tif joy_buttons:\n\t\t_state.joy_button_state_changed.disconnect(_refresh)\n\tif joy_axes:\n\t\t_state.joy_axis_state_changed.disconnect(_refresh)\n\tif touch:\n\t\t_state.touch_state_changed.disconnect(_refresh)\n\t_state.application_focus_lost.disconnect(_on_application_focus_lost)\n\nfunc _on_application_focus_lost() -> void:\n\t# Clear our value directly rather than going through _refresh(), because\n\t# _refresh() will not update the value if it is already non-zero (by design,\n\t# to keep fast inputs alive for the full frame). Focus loss is not an input\n\t# event — the state has already been cleared in GUIDEInputState — so we must\n\t# bypass that guard and clear immediately.\n\t_value = Vector3.ZERO\n\nfunc _refresh() -> void:\n\t# if the input was already actuated this frame, remain\n\t# actuated, even if more input events come in. Input will\n\t# reset at the end of the frame.\n\tif not _value.is_zero_approx():\n\t\treturn\n\t\n\tif keyboard and _state.is_any_key_pressed():\t\t\n\t\t_value = Vector3.RIGHT\n\t\treturn\n\n\tif mouse_buttons and _state.is_any_mouse_button_pressed():\n\t\t_value = Vector3.RIGHT\n\t\treturn\n\t\n\tif mouse_movement and _state.get_mouse_delta_since_last_frame().length() >= minimum_mouse_movement_distance:\n\t\t_value = Vector3.RIGHT\n\t\treturn\n\t\t\n\tif joy_buttons and _state.is_any_joy_button_pressed():\n\t\t_value = Vector3.RIGHT\n\t\treturn\n\n\tif joy_axes and _state.is_any_joy_axis_actuated(minimum_joy_axis_actuation_strength):\n\t\t_value = Vector3.RIGHT\n\t\treturn\n\t\t\n\tif touch and _state.is_any_finger_down():\n\t\t_value = Vector3.RIGHT\n\t\treturn\n\t\t\n\t_value = Vector3.ZERO\t\t\n\n\nfunc is_same_as(other:GUIDEInput) -> bool:\n\treturn other is GUIDEInputAny and \\\n\t\tmouse_buttons == other.mouse_buttons and \\\n\t\tmouse_movement == other.mouse_movement and \\\n\t\tjoy_buttons == other.joy_buttons and \\\n\t\tjoy_axes == other.joy_axes and \\\n\t\tkeyboard == other.keyboard and \\\n\t\ttouch == other.touch and \\\n\t\tis_equal_approx(minimum_mouse_movement_distance, other.minimum_mouse_movement_distance) and \\\n\t\tis_equal_approx(minimum_joy_axis_actuation_strength, other.minimum_joy_axis_actuation_strength)\n\nfunc _editor_name() -> String:\n\treturn \"Any Input\"\n\t\n\t\nfunc _editor_description() -> String:\n\treturn \"Input that triggers if any input from the given device class is given.\"\n\t\n\t\nfunc _native_value_type() -> GUIDEAction.GUIDEActionValueType:\n\treturn GUIDEAction.GUIDEActionValueType.BOOL\n\n# support for legacy properties\nfunc _get_property_list() -> Array[Dictionary]:\n\treturn [\n\t\t{\n\t\t\t\"name\": \"mouse\",\n\t\t\t\"type\": TYPE_BOOL,\n\t\t\t\"usage\": PROPERTY_USAGE_NO_EDITOR\n\t\t},\n\t\t{\n\t\t\t\"name\": \"joy\",\n\t\t\t\"type\": TYPE_BOOL,\n\t\t\t\"usage\": PROPERTY_USAGE_NO_EDITOR\n\t\t}\n\t]\n\t\nfunc _device_type() -> DeviceType:\n\tvar result:DeviceType = DeviceType.NONE\n\tif joy_axes or joy_buttons:\n\t\tresult |= DeviceType.JOY\n\tif mouse_buttons or mouse_movement:\n\t\tresult |= DeviceType.MOUSE\n\tif keyboard:\n\t\tresult |= DeviceType.KEYBOARD\n\tif touch:\n\t\tresult |= DeviceType.TOUCH\n\t\t\n\treturn result\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_any.gd.uid",
    "content": "uid://w3fbpe7r01n8\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_joy_axis_1d.gd",
    "content": "## Input from a single joy axis.\n@tool\nclass_name GUIDEInputJoyAxis1D\nextends GUIDEInputJoyBase\n\n## The joy axis to sample\n@export var axis:JoyAxis = JOY_AXIS_LEFT_X:\n\tset(value):\n\t\tif value == axis:\n\t\t\treturn\n\t\taxis = value\n\t\temit_changed()\t\n\nfunc _begin_usage() -> void:\n\t_state.joy_axis_state_changed.connect(_refresh)\n\t\nfunc _end_usage() -> void:\n\t_state.joy_axis_state_changed.disconnect(_refresh)\n\t\nfunc _refresh() -> void:\n\t_value.x = _state.get_joy_axis_value(joy_index, axis)\n\n\nfunc is_same_as(other:GUIDEInput) -> bool:\n\treturn other is GUIDEInputJoyAxis1D and \\\n\t\tother.axis == axis and \\\n\t\tother.joy_index == joy_index\n\nfunc _to_string() -> String:\n\treturn \"(GUIDEInputJoyAxis1D: axis=\" + str(axis) + \", joy_index=\"  + str(joy_index) + \")\"\n\nfunc _editor_name() -> String:\n\treturn \"Joy Axis 1D\"\n\t\nfunc _editor_description() -> String:\n\treturn \"The input from a single joy axis.\"\n\t\n\nfunc _native_value_type() -> GUIDEAction.GUIDEActionValueType:\n\treturn GUIDEAction.GUIDEActionValueType.AXIS_1D\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_joy_axis_1d.gd.uid",
    "content": "uid://bbhoxsiqwo07l\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_joy_axis_2d.gd",
    "content": "## Input from two joy axes.\nclass_name GUIDEInputJoyAxis2D\nextends GUIDEInputJoyBase\n\n## The joy axis to sample for x input.\n@export var x:JoyAxis = JOY_AXIS_LEFT_X:\n\tset(value):\n\t\tif value == x:\n\t\t\treturn\n\t\tx = value\n\t\temit_changed()\n\t\t\n\t\t\n## The joy axis to sample for y input.\n@export var y:JoyAxis = JOY_AXIS_LEFT_Y:\n\tset(value):\n\t\tif value == y:\n\t\t\treturn\n\t\ty = value\n\t\temit_changed()\n\nfunc _begin_usage() -> void:\n\t_state.joy_axis_state_changed.connect(_refresh)\n\t_refresh()\n\t\nfunc _end_usage() -> void:\t\n\t_state.joy_axis_state_changed.disconnect(_refresh)\n\n\t\nfunc _refresh():\n\t_value.x = _state.get_joy_axis_value(joy_index, x)\n\t_value.y = _state.get_joy_axis_value(joy_index, y)\n\t\n\t\nfunc is_same_as(other:GUIDEInput) -> bool:\n\treturn other is GUIDEInputJoyAxis2D and \\\n\t\tother.x == x and \\\n\t\tother.y == y and \\\n\t\tother.joy_index == joy_index\n\nfunc _to_string() -> String:\n\treturn \"(GUIDEInputJoyAxis2D: x=\" + str(x) + \", y=\" + str(y) + \", joy_index=\"  + str(joy_index) + \")\"\n\n\nfunc _editor_name() -> String:\n\treturn \"Joy Axis 2D\"\n\t\nfunc _editor_description() -> String:\n\treturn \"The input from two Joy axes. Usually from a stick.\"\n\t\n\nfunc _native_value_type() -> GUIDEAction.GUIDEActionValueType:\n\treturn GUIDEAction.GUIDEActionValueType.AXIS_2D\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_joy_axis_2d.gd.uid",
    "content": "uid://doauobik3xyea\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_joy_base.gd",
    "content": "## Base class for joystick inputs.\n@tool\nclass_name GUIDEInputJoyBase\nextends GUIDEInput\n\n## The index of the connected joy pad to check. \n## -1 = Any connected joy pad\n##  0 = First connected joy pad\n##  1 = Second connected joy pad\n##  2 = Third connected joy pad\n##  3 = Fourth connected joy pad\n## -2 = First virtual joy pad\n## -3 = Second virtual joy pad\n@export_enum(\"Any:-1\",\"1:0\",\"2:1\",\"3:2\",\"4:3\",\"Virtual 1:-2\",\"Virtual 2:-3\", \"Virtual 3:-4\", \"Virtual 4:-5\") var joy_index:int = -1:\n\tset(value):\n\t\tif value == joy_index:\n\t\t\treturn\n\t\tjoy_index = value\n\t\temit_changed()\t\n\nfunc _device_type() -> DeviceType:\n\treturn DeviceType.JOY\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_joy_base.gd.uid",
    "content": "uid://cnqnbdsw3lw12\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_joy_button.gd",
    "content": "@tool\nclass_name GUIDEInputJoyButton\nextends GUIDEInputJoyBase\n\n@export var button:JoyButton = JOY_BUTTON_A:\n\tset(value):\n\t\tif value == button:\n\t\t\treturn\n\t\tbutton = value\n\t\temit_changed()\t\t\n\nfunc _begin_usage() -> void:\n\t_state.joy_button_state_changed.connect(_refresh)\n\t_refresh()\n\t\nfunc _end_usage() -> void:\n\t_state.joy_button_state_changed.disconnect(_refresh)\n\nfunc _refresh():\n\t_value.x = 1.0 if _state.is_joy_button_pressed(joy_index, button) else 0.0\n\n\t\nfunc is_same_as(other:GUIDEInput) -> bool:\n\treturn other is GUIDEInputJoyButton and \\\n\t\t other.button == button and \\\n\t\t other.joy_index == joy_index\n\n\nfunc _to_string() -> String:\n\treturn \"(GUIDEInputJoyButton: button=\" + str(button) + \", joy_index=\"  + str(joy_index) + \")\"\n\n\nfunc _editor_name() -> String:\n\treturn \"Joy Button\"\n\t\nfunc _editor_description() -> String:\n\treturn \"A button press from a joy button.\"\n\t\n\nfunc _native_value_type() -> GUIDEAction.GUIDEActionValueType:\n\treturn GUIDEAction.GUIDEActionValueType.BOOL\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_joy_button.gd.uid",
    "content": "uid://rvttn472ix6v\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_key.gd",
    "content": "@tool\nclass_name GUIDEInputKey\nextends GUIDEInput\n\n## The physical keycode of the key.\n@export var key:Key:\n\tset(value):\n\t\tif value == key:\n\t\t\treturn\n\t\tkey = value\n\t\temit_changed()\t\n\t\t\n\n@export_group(\"Modifiers\")\n## Whether shift must be pressed.\n@export var shift:bool = false:\n\tset(value):\n\t\tif value == shift:\n\t\t\treturn\n\t\tshift = value\n\t\temit_changed()\t\n\n## Whether control must be pressed.\n@export var control:bool = false:\n\tset(value):\n\t\tif value == control:\n\t\t\treturn\n\t\tcontrol = value\n\t\temit_changed()\t\n\t\t\n## Whether alt must be pressed.\n@export var alt:bool = false:\n\tset(value):\n\t\tif value == alt:\n\t\t\treturn\n\t\talt = value\n\t\temit_changed()\t\t\n\t\n\t\t\n## Whether meta/win/cmd must be pressed.\n@export var meta:bool = false:\n\tset(value):\n\t\tif value == meta:\n\t\t\treturn\n\t\tmeta = value\n\t\temit_changed()\t\n\n## Whether this input should fire if additional\n## modifier keys are currently pressed.\t\t\n@export var allow_additional_modifiers:bool = true:\n\tset(value):\n\t\tif value == allow_additional_modifiers:\n\t\t\treturn\n\t\tallow_additional_modifiers = value\n\t\temit_changed()\n\t\t\t\t\t\n## Helper array. All keys that must be pressed for this input to considered actuated.\nvar _must_be_pressed:Array[Key] = []\n## Helper array. All keys that must not be pressed for this input to considered actuated.\nvar _must_not_be_pressed:Array[Key] = []\n\nfunc _begin_usage() -> void:\n\t_must_be_pressed = [key]\n\t\n\t# also add the modifiers to the list of keys that must be pressed\n\tif shift:\n\t\t_must_be_pressed.append(KEY_SHIFT)\n\tif control:\n\t\t_must_be_pressed.append(KEY_CTRL)\n\tif alt:\n\t\t_must_be_pressed.append(KEY_ALT)\n\tif meta:\n\t\t_must_be_pressed.append(KEY_META)\n\t\t\n\t_must_not_be_pressed = []\n\t# now unless additional modifiers are allowed, add all modifiers\n\t# that are not required to the list of keys that must not be pressed\n\t# except if the bound key is actually the modifier itself\n\tif not allow_additional_modifiers:\n\t\tif not shift and key != KEY_SHIFT:\n\t\t\t_must_not_be_pressed.append(KEY_SHIFT)\n\t\tif not control and key != KEY_CTRL:\n\t\t\t_must_not_be_pressed.append(KEY_CTRL)\n\t\tif not alt and key != KEY_ALT:\n\t\t\t_must_not_be_pressed.append(KEY_ALT)\n\t\tif not meta and key != KEY_META:\n\t\t\t_must_not_be_pressed.append(KEY_META)\n\t\t\t\n\t# subscribe to input events\n\t_state.keyboard_state_changed.connect(_refresh)\n\t_refresh()\n\t\nfunc _end_usage() -> void:\n\t# unsubscribe from input events\n\t_state.keyboard_state_changed.disconnect(_refresh)\n\t\n\t\nfunc _refresh():\n\t# We are actuated if all keys that must be pressed are pressed and none of the keys that must not be pressed\n\t# are pressed. \n\tvar is_actuated:bool = _state.are_all_keys_pressed(_must_be_pressed) and not _state.is_at_least_one_key_pressed(_must_not_be_pressed)\n\t_value.x = 1.0 if is_actuated else 0.0\n\t\n\nfunc is_same_as(other:GUIDEInput) -> bool:\n\treturn other is GUIDEInputKey \\\n\t\t\tand other.key == key \\\n\t\t\tand other.shift == shift \\\n\t\t\tand other.control == control \\\n\t\t\tand other.alt == alt \\\n\t\t\tand other.meta == meta \\\n\t\t\tand other.allow_additional_modifiers == allow_additional_modifiers\n\nfunc _to_string() -> String:\n\treturn \"(GUIDEInputKey: key=\" + str(key) + \", shift=\"  + str(shift) + \", alt=\" + str(alt) + \", control=\" + str(control) + \", meta=\"+ str(meta) + \")\"\n\n\nfunc _editor_name() -> String:\n\treturn \"Key\"\n\t\nfunc _editor_description() -> String:\n\treturn \"A button press on the keyboard.\"\n\t\n\nfunc _native_value_type() -> GUIDEAction.GUIDEActionValueType:\n\treturn GUIDEAction.GUIDEActionValueType.BOOL\n\nfunc _device_type() -> DeviceType:\n\treturn DeviceType.KEYBOARD\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_key.gd.uid",
    "content": "uid://cw71o87tvdx3q\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_mouse_axis_1d.gd",
    "content": "@tool\nclass_name GUIDEInputMouseAxis1D\nextends GUIDEInput\n\nenum GUIDEInputMouseAxis {\n\tX,\n\tY\n}\n\n@export var axis:GUIDEInputMouseAxis:\n\tset(value):\n\t\tif value == axis:\n\t\t\treturn\n\t\taxis = value\n\t\temit_changed()\t\t\n\n# we don't get mouse updates when the mouse is not moving, so this needs to be \n# reset every frame\nfunc _needs_reset() -> bool:\n\treturn true\n\n\nfunc _begin_usage() -> void:\n\t# subscribe to mouse movement events\n\t_state.mouse_position_changed.connect(_refresh)\n\t_refresh()\n\t\nfunc _end_usage() -> void:\n\t# unsubscribe from mouse movement events\n\t_state.mouse_position_changed.disconnect(_refresh)\n\nfunc _refresh() -> void:\n\tvar delta:Vector2 = _state.get_mouse_delta_since_last_frame()\t\n\tmatch axis:\n\t\tGUIDEInputMouseAxis.X:\n\t\t\t_value.x = delta.x\n\t\tGUIDEInputMouseAxis.Y:\n\t\t\t_value.x = delta.y\n\n\t\t\nfunc is_same_as(other:GUIDEInput) -> bool:\n\treturn other is GUIDEInputMouseAxis1D and other.axis == axis\n\nfunc _to_string() -> String:\n\treturn \"(GUIDEInputMouseAxis1D: axis=\" + str(axis) + \")\"\n\n\nfunc _editor_name() -> String:\n\treturn \"Mouse Axis 1D\"\n\t\n\t\nfunc _editor_description() -> String:\n\treturn \"Relative mouse movement on a single axis.\"\n\t\n\nfunc _native_value_type() -> GUIDEAction.GUIDEActionValueType:\n\treturn GUIDEAction.GUIDEActionValueType.AXIS_1D\n\nfunc _device_type() -> DeviceType:\n\treturn DeviceType.MOUSE\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_mouse_axis_1d.gd.uid",
    "content": "uid://b6bwb7ie85kl1\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_mouse_axis_2d.gd",
    "content": "@tool\nclass_name GUIDEInputMouseAxis2D\nextends GUIDEInput\n\n\n# we don't get mouse updates when the mouse is not moving, so this needs to be \n# reset every frame\nfunc _needs_reset() -> bool:\n\treturn true\n\nfunc _begin_usage() -> void:\n\t# subscribe to mouse movement events\n\t_state.mouse_position_changed.connect(_refresh)\n\t_refresh()\n\nfunc _end_usage() -> void:\n\t# unsubscribe from mouse movement events\n\t_state.mouse_position_changed.disconnect(_refresh)\n\nfunc _refresh() -> void:\n\tvar delta:Vector2 = _state.get_mouse_delta_since_last_frame()\n\t_value.x = delta.x\n\t_value.y = delta.y\n\t\t\nfunc is_same_as(other:GUIDEInput) -> bool:\n\treturn other is GUIDEInputMouseAxis2D\n\n\nfunc _to_string() -> String:\n\treturn \"(GUIDEInputMouseAxis2D)\"\n\n\nfunc _editor_name() -> String:\n\treturn \"Mouse Axis 2D\"\n\n\t\nfunc _editor_description() -> String:\n\treturn \"Relative mouse movement on 2 axes.\"\n\n\nfunc _native_value_type() -> GUIDEAction.GUIDEActionValueType:\n\treturn GUIDEAction.GUIDEActionValueType.AXIS_2D\n\nfunc _device_type() -> DeviceType:\n\treturn DeviceType.MOUSE\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_mouse_axis_2d.gd.uid",
    "content": "uid://dh0hf08e0yit5\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_mouse_button.gd",
    "content": "@tool\nclass_name GUIDEInputMouseButton\nextends GUIDEInput\n\n@export var button: MouseButton = MOUSE_BUTTON_LEFT:\n\tset(value):\n\t\tif value == button:\n\t\t\treturn\n\t\tbutton = value\n\t\temit_changed()\n\n# The value that this input will be reset to at the end of the frame.\nvar _reset_to: Vector3\nvar _was_pressed_this_frame: bool\n\n\nfunc _needs_reset() -> bool:\n\t# mouse wheel up and down can potentially send multiple inputs within a single frame\n\t# so we need to smooth this out a bit.\n\treturn button == MOUSE_BUTTON_WHEEL_UP or button == MOUSE_BUTTON_WHEEL_DOWN\n\n\nfunc _reset() -> void:\n\t_was_pressed_this_frame = false\n\t_value = _reset_to\n\n\nfunc _begin_usage() -> void:\n\t# subscribe to mouse button events\n\t_state.mouse_button_state_changed.connect(_refresh)\n\t_refresh()\n\n\nfunc _end_usage() -> void:\n\t# unsubscribe from mouse button events\n\t_state.mouse_button_state_changed.disconnect(_refresh)\n\n\nfunc _refresh() -> void:\n\tvar is_pressed: bool = _state.is_mouse_button_pressed(button)\n\n\tif _needs_reset():\n\t\t# we always reset to the last event we received in a frame\n\t\t# so after the frame is over we're still in sync.\n\t\t_reset_to.x =  1.0 if is_pressed else 0.0\n\n\t\tif is_pressed:\n\t\t\t_was_pressed_this_frame = true\n\n\t\tif not is_pressed and _was_pressed_this_frame:\n\t\t\t# keep pressed state for this frame\n\t\t\treturn\n\n\t_value.x = 1.0 if is_pressed else 0.0\n\n\nfunc is_same_as(other: GUIDEInput) -> bool:\n\treturn other is GUIDEInputMouseButton and other.button == button\n\n\nfunc _to_string() -> String:\n\treturn \"(GUIDEInputMouseButton: button=\" + str(button) + \")\"\n\n\nfunc _editor_name() -> String:\n\treturn \"Mouse Button\"\n\n\nfunc _editor_description() -> String:\n\treturn \"A press of a mouse button. The mouse wheel is also a button.\"\n\n\nfunc _native_value_type() -> GUIDEAction.GUIDEActionValueType:\n\treturn GUIDEAction.GUIDEActionValueType.BOOL\n\nfunc _device_type() -> DeviceType:\n\treturn DeviceType.MOUSE\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_mouse_button.gd.uid",
    "content": "uid://vgjlx6p007lp\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_mouse_position.gd",
    "content": "@tool\nclass_name GUIDEInputMousePosition\nextends GUIDEInput\n\n\nfunc _begin_usage() -> void :\n\t# subscribe to mouse movement events\n\t_state.mouse_position_changed.connect(_refresh)\n\t_refresh()\n\t\nfunc _end_usage() -> void:\n\t# unsubscribe from mouse movement events\n\t_state.mouse_position_changed.disconnect(_refresh)\n\nfunc _refresh():\n\tvar position:Vector2 = _state.get_mouse_position()\n\n\t_value.x = position.x\n\t_value.y = position.y\n\t\t\n\t\t\nfunc is_same_as(other:GUIDEInput) -> bool:\n\treturn other is GUIDEInputMousePosition\n\n\nfunc _to_string() -> String:\n\treturn \"(GUIDEInputMousePosition)\"\n\n\nfunc _editor_name() -> String:\n\treturn \"Mouse Position\"\n\n\t\nfunc _editor_description() -> String:\n\treturn \"Position of the mouse in the main viewport.\"\n\n\nfunc _native_value_type() -> GUIDEAction.GUIDEActionValueType:\n\treturn GUIDEAction.GUIDEActionValueType.AXIS_2D\n\nfunc _device_type() -> DeviceType:\n\treturn DeviceType.MOUSE\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_mouse_position.gd.uid",
    "content": "uid://deeru16npi81q\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_state.gd",
    "content": "## The GUIDEInputState holds the current state of all input. It is basically a wrapper around Godot's Input\n## class that provides some additional functionality like getting the information if any key or mouse button\n## is currently pressed. It also is the single entry point for all input events from Godot, so we don't have \n## process them in every GUIDEInput object and duplicate input handling code everywere. This also improves performance.\n## \nclass_name GUIDEInputState\n\n## Device ID for a virtual joystick that means \"any joystick\".\n## This relies on the fact that Godot's device IDs for joysticks are always >= 0.\n## https://github.com/godotengine/godot/blob/80a3d205f1ad22e779a64921fb56d62b893881ae/core/input/input.cpp#L1821\nconst ANY_JOY_DEVICE_ID: int = -1\n\n## We assign a virtual device ID for the virtual joystick inputs.\n## Virtual joystick device IDs will be negative, starting with -2 and going down from there.\n## This relies on the fact that Godot's device IDs for joysticks are always >= 0.\nconst VIRTUAL_JOY_DEVICE_ID_OFFSET: int = -2\n\n## The set of currently connected virtual joy devices. Key is the device id,\n## value is the number of virtual sticks connected with this device id.\nvar _virtual_joy_devices:Dictionary = {}\n\n## Signalled, when the keyboard state has changed.\nsignal keyboard_state_changed()\n## Signalled, when the mouse motion state has changed.\nsignal mouse_position_changed()\n## Signalled, when the mouse button state has changed.\nsignal mouse_button_state_changed()\n## Signalled, when the joy button state has changed.\nsignal joy_button_state_changed()\n## Signalled, when the joy axis state has changed.\nsignal joy_axis_state_changed()\n## Signalled, when the touch state has changed.\nsignal touch_state_changed()\n## Signalled when the application loses focus. Unlike the other signals above,\n## this is not triggered by an InputEvent — Godot clears its own input state\n## directly in Input::release_pressed_events() (core/input/input.cpp) without\n## dispatching any events. Inputs that maintain their own internal state should\n## listen to this signal and reset it immediately when focus is lost.\n## See: https://github.com/godotneers/G.U.I.D.E/issues/189\nsignal application_focus_lost()\n\n# Keys that are currently pressed. Key is the key index, value is not important. The presence of a key in the dictionary\n# indicates that the key is currently pressed.\nvar _keys: Dictionary = {}\n# Fingers that are currently touching the screen. Key is the finger index, value is the position (Vector2).\nvar _finger_positions: Dictionary = {}\n# The mouse movement since the last frame. \nvar _mouse_movement: Vector2 = Vector2.ZERO\n# Mouse buttons that are currently pressed. Key is the button index, value is not important. The presence of a key\n# in the dictionary indicates that the button is currently pressed.\nvar _mouse_buttons: Dictionary = {}\n# Joy buttons that are currently pressed. Key is device id, value is a dictionary with the button index as key. The \n# value of the inner dictionary is not important. The presence of a key in the inner dictionary indicates that the button \n# is currently pressed.\nvar _joy_buttons: Dictionary = {}\n# Current values of joy axes. Key is device id, value is a dictionary with the axis index as key. \n# The value of the inner dictionary is the axis value. Once an axis is actuated, it will be added to the dictionary.\n# We will not remove it anymore after that.\nvar _joy_axes: Dictionary = {}\n\n# The current mapping of joy index to device id. This is used to map the joy index to the device id. A joy index\n# if -1 means \"any device id\".\nvar _joy_index_to_device_id: Dictionary = {}\n\n# This holds the state of keys that have changed this frame. The key is the key, the value is true if the key\n# was last pressed and false if it was last released.\nvar _pending_keys:Dictionary = {}\n# This holds the state of mouse buttons that have changed this frame. The key is the mouse button index, the value is\n# true, if the mouse button was last pressed and false if it was last released.\nvar _pending_mouse_buttons:Dictionary = {}\n# This holds the state of joy buttons that have changed this frame. The key is the joy device id, the value is\n# a nested dictionary. The nested dictionary has the button index as key and true as value if the button was last\n# pressed or false if it was last released.\nvar _pending_joy_buttons:Dictionary = {}\n\nfunc _init():\n\tInput.joy_connection_changed.connect(_refresh_joy_device_ids)\n\t_clear()\n\n\n## Connects a new virtual joystick and returns its device id.\n## The returned device id will be negative, starting with -2 and going down from there.\n## Since virtual sticks are UI components and not real hardware, we need to give the\n## UI elements the chance to tell to which virtual stick they belong. For this\n## we introduce the stick_index. Any UI element tells which virtual stick it belongs to\n## by providing the same stick_index.\nfunc connect_virtual_stick(stick_index:int) -> int:\n\t# we treat an invalid stick index as a stick index of 0 but print an error\n\t# to let the user know something is wrong\n\tif stick_index < 0:\n\t\tpush_error(\"Invalid stick index \" + str(stick_index) + \" for virtual stick. Must be >= 0.\")\n\t\tstick_index = 0\t\n\n\tvar device_id:int = VIRTUAL_JOY_DEVICE_ID_OFFSET - stick_index\n\tif _virtual_joy_devices.has(device_id):\n\t\t# just record the additional connection and return the existing device id\n\t\t_virtual_joy_devices[device_id] += 1\n\t\treturn device_id\n\t\n\t# new device\n\t_virtual_joy_devices[device_id] = 1\n\t\n\t_refresh_joy_device_ids(0, 0)\n\t\n\treturn device_id\n\t\n\t\n## Disconnects the virtual joystick with the given device id.\n## If no such device is connected, nothing happens.\nfunc disconnect_virtual_stick(device_id:int) -> void:\n\tif not _virtual_joy_devices.has(device_id):\n\t\treturn\n\t\t\n\tvar count:int = _virtual_joy_devices[device_id]\n\tif count > 1:\n\t\t# just reduce the connection count, but don't remove the device yet\n\t\t_virtual_joy_devices[device_id] -= 1\n\t\treturn\n\t\t\n\t# last connection went away, so we can remove the device\t\n\t_virtual_joy_devices.erase(device_id)\n\t_joy_index_to_device_id.erase(device_id)\n\t\n\tif _joy_buttons.has(device_id):\n\t\t_joy_buttons.erase(device_id)\n\t\t_recalculate_any_joy_buttons()\n\t\tjoy_button_state_changed.emit()\n\n\tif _joy_axes.has(device_id):\n\t\t_joy_axes.erase(device_id)\n\t\t_recalculate_any_joy_axes()\n\t\tjoy_axis_state_changed.emit()\n\n## Recalculates a specific button state for ANY_JOY_DEVICE_ID based on all connected devices.\nfunc _recalculate_any_joy_button(button: int) -> void:\n\tvar any_value: bool = false\n\tfor device_id in _joy_buttons.keys():\n\t\tif device_id != ANY_JOY_DEVICE_ID and _joy_buttons[device_id].has(button):\n\t\t\tany_value = true\n\t\t\tbreak\n\n\tif any_value:\n\t\t_joy_buttons[ANY_JOY_DEVICE_ID][button] = true\n\telse:\n\t\t_joy_buttons[ANY_JOY_DEVICE_ID].erase(button)\n\n\n## Recalculates all button states for ANY_JOY_DEVICE_ID based on all connected devices.\nfunc _recalculate_any_joy_buttons() -> void:\n\t_joy_buttons[ANY_JOY_DEVICE_ID].clear()\n\tfor device_id in _joy_buttons.keys():\n\t\tif device_id != ANY_JOY_DEVICE_ID:\n\t\t\tfor button in _joy_buttons[device_id].keys():\n\t\t\t\t_joy_buttons[ANY_JOY_DEVICE_ID][button] = true\n\n\n## Recalculates a specific axis value for ANY_JOY_DEVICE_ID based on all connected devices.\n## Uses the maximum actuation across all devices.\nfunc _recalculate_any_joy_axis(axis: int) -> void:\n\tvar any_value: float = 0.0\n\tvar maximum_actuation: float = 0.0\n\tfor device_id in _joy_axes.keys():\n\t\tif device_id != ANY_JOY_DEVICE_ID and _joy_axes[device_id].has(axis):\n\t\t\tvar strength: float = abs(_joy_axes[device_id][axis])\n\t\t\tif strength > maximum_actuation:\n\t\t\t\tmaximum_actuation = strength\n\t\t\t\tany_value = _joy_axes[device_id][axis]\n\n\t_joy_axes[ANY_JOY_DEVICE_ID][axis] = any_value\n\n\n## Recalculates all axis values for ANY_JOY_DEVICE_ID based on all connected devices.\nfunc _recalculate_any_joy_axes() -> void:\n\t# Collect all unique axes that have been actuated on any device\n\tvar all_axes: Dictionary = {}\n\tfor device_id in _joy_axes.keys():\n\t\tif device_id != ANY_JOY_DEVICE_ID:\n\t\t\tfor axis in _joy_axes[device_id].keys():\n\t\t\t\tall_axes[axis] = true\n\n\t# Recalculate each axis\n\t_joy_axes[ANY_JOY_DEVICE_ID].clear()\n\tfor axis in all_axes.keys():\n\t\t_recalculate_any_joy_axis(axis)\n\n\n# Used by the automated tests to make sure we don't have any leftovers from the\n# last test.\nfunc _clear():\n\t_keys.clear()\n\t_finger_positions.clear()\n\t_mouse_movement = Vector2.ZERO\n\t_mouse_buttons.clear()\n\t_joy_buttons.clear()\n\t_joy_axes.clear()\n\n\t_refresh_joy_device_ids(0, 0)\n\n\t# ensure we have an entry for the virtual \"any device id\"\n\t_joy_buttons[ANY_JOY_DEVICE_ID] = {}\n\t_joy_axes[ANY_JOY_DEVICE_ID] = {}\n\t\n\t# also clear all virtual joy devices, these can be set up again by the next test\n\tfor device_id in _virtual_joy_devices.keys():\n\t\t_joy_index_to_device_id.erase(device_id)\n\t\n\t_virtual_joy_devices.clear()\n\t\n\t# pending states are created on demand, so we don't need to clear them here\n\n\n# Called when any joy device is connected or disconnected. This will refresh the joy device ids and clear out any\n# joy state which is not valid anymore. Will also notify relevant inputs.\nfunc _refresh_joy_device_ids(_ignore1, _ignore2):\n\t# refresh the joy device ids\n\t_joy_index_to_device_id.clear()\n\t# get the real joys from the input system\n\tvar connected_joys:Array[int] = Input.get_connected_joypads()\n\t# append the currently connected virtual joys\n\t\n\tconnected_joys.append_array(_virtual_joy_devices.keys())\n\tfor i in connected_joys.size():\n\t\tvar device_id:int = connected_joys[i]\n\t\tif device_id > 0:\n\t\t\t# godot's joys\n\t\t\t_joy_index_to_device_id[i] = device_id\n\t\telse:\n\t\t\t# virtual joys\n\t\t\t_joy_index_to_device_id[device_id] = device_id\n\t\t\t\n\t\t# ensure we have an inner dictionary for the device id\n\t\t# by setting this here, we don't need to check for the device id\n\t\t# on every input event\n\t\tif not _joy_buttons.has(device_id):\n\t\t\t_joy_buttons[device_id] = {}\n\t\tif not _joy_axes.has(device_id):\n\t\t\t_joy_axes[device_id] = {}\n\t\tif not _pending_joy_buttons.has(device_id):\n\t\t\t_pending_joy_buttons[device_id] = {}\n\n\t# add a virtual device id for the \"any device id\" case\n\t_joy_index_to_device_id[-1] = ANY_JOY_DEVICE_ID\n\n\tfor device_id in _pending_joy_buttons.keys():\n\t\tif device_id != ANY_JOY_DEVICE_ID and not connected_joys.has(device_id):\n\t\t\t_pending_joy_buttons.erase(device_id)\n\n\tvar dirty: bool = false\n\t# clear out any joy state which is not valid anymore\n\tfor device_id in _joy_buttons.keys():\n\t\tif device_id != ANY_JOY_DEVICE_ID and not connected_joys.has(device_id):\n\t\t\tdirty = true\n\t\t\t_joy_buttons.erase(device_id)\n\n\tif dirty:\n\t\t_recalculate_any_joy_buttons()\n\t\t# notify all inputs that the joy state has changed\n\t\tjoy_button_state_changed.emit()\n\n\tdirty = false\n\tfor device_id in _joy_axes.keys():\n\t\tif device_id != ANY_JOY_DEVICE_ID and not connected_joys.has(device_id):\n\t\t\tdirty = true\n\t\t\t_joy_axes.erase(device_id)\n\n\tif dirty:\n\t\t_recalculate_any_joy_axes()\n\t\t# notify all inputs that the joy state has changed\n\t\tjoy_axis_state_changed.emit()\n\n\n## Called when the application loses focus. Clears input state that Godot\n## itself clears in Input::release_pressed_events() (core/input/input.cpp).\n##\n## Godot does this by directly zeroing its internal arrays — no InputEvents\n## are ever dispatched. Because G.U.I.D.E maintains its own shadow state\n## (_keys, _mouse_buttons, etc.) that is only updated from incoming events,\n## we must mirror this cleanup here or stale state will cause inputs like\n## GUIDEInputAny to keep firing every frame after focus returns.\n##\n## What Godot clears (and so do we):\n##   - keys_pressed / physical_keys_pressed / key_label_pressed  → always\n##\n## What Godot does NOT clear (and neither do we, to stay consistent):\n##   - mouse button state — left as-is by Godot on focus loss\n##\n## See: https://github.com/godotneers/G.U.I.D.E/issues/189\nfunc focus_lost() -> void:\n\t# Discard any key events that arrived this frame but haven't been\n\t# committed to _keys yet, then clear the committed state.\n\t_pending_keys.clear()\n\tif not _keys.is_empty():\n\t\t_keys.clear()\n\t\tkeyboard_state_changed.emit()\n\n\t# Mirror Godot's conditional joy clearing: Input::release_pressed_events()\n\t# only clears joy state when ignore_joypad_on_unfocused_application is set\n\t# (input_devices/joypads/ignore_joypad_on_unfocused_application in project settings).\n\tif ProjectSettings.get_setting(\"input_devices/joypads/ignore_joypad_on_unfocused_application\", false):\n\t\tfor device_id in _pending_joy_buttons:\n\t\t\t_pending_joy_buttons[device_id].clear()\n\n\t\tvar joy_buttons_dirty := false\n\t\tfor device_id in _joy_buttons:\n\t\t\tif not _joy_buttons[device_id].is_empty():\n\t\t\t\t_joy_buttons[device_id].clear()\n\t\t\t\tjoy_buttons_dirty = true\n\t\tif joy_buttons_dirty:\n\t\t\tjoy_button_state_changed.emit()\n\n\t\tvar joy_axes_dirty := false\n\t\tfor device_id in _joy_axes:\n\t\t\tif not _joy_axes[device_id].is_empty():\n\t\t\t\t_joy_axes[device_id].clear()\n\t\t\t\tjoy_axes_dirty = true\n\t\tif joy_axes_dirty:\n\t\t\tjoy_axis_state_changed.emit()\n\n\tapplication_focus_lost.emit()\n\n\n## Called at the end of the frame to reset the state before the next frame.\nfunc _reset() -> void:\n\t_mouse_movement = Vector2.ZERO\n\n\t# apply pending key state at end of the frame.\n\tfor key in _pending_keys.keys():\n\t\tvar is_down = _pending_keys[key]\n\t\tif is_down and not _keys.has(key):\n\t\t\t_keys[key] = true\n\t\t\t# we emit one change event per changed key just like it would happen\n\t\t\t# as if the keys were not pressed very fast. this is to ensure same\n\t\t\t# execution order of things, so everything stays predictable\n\t\t\tkeyboard_state_changed.emit()\n\t\telif not is_down and _keys.has(key):\n\t\t\t_keys.erase(key)\n\t\t\tkeyboard_state_changed.emit()\n\n\t_pending_keys.clear()\n\t\n\t# apply pending mouse button state\n\tfor button in _pending_mouse_buttons.keys():\n\t\tvar is_down = _pending_mouse_buttons[button]\n\t\tif is_down and not _mouse_buttons.has(button):\n\t\t\t_mouse_buttons[button] = true\n\t\t\tmouse_button_state_changed.emit()\n\t\telif not is_down and _mouse_buttons.has(button):\n\t\t\t_mouse_buttons.erase(button)\n\t\t\tmouse_button_state_changed.emit()\n\t\t\t\n\t_pending_mouse_buttons.clear()\n\t\n\t# apply pending joy button state\n\tfor joy in _pending_joy_buttons.keys():\n\t\tfor button in _pending_joy_buttons[joy]:\n\t\t\tvar changed:bool = false\n\t\t\tvar is_down = _pending_joy_buttons[joy][button]\n\t\t\tif is_down and not _joy_buttons[joy].has(button):\n\t\t\t\t_joy_buttons[joy][button] = true\n\t\t\t\tchanged = true\n\t\t\telif not is_down and _joy_buttons[joy].has(button):\n\t\t\t\t_joy_buttons[joy].erase(button)\n\t\t\t\tchanged = true\n\t\n\t\t\t# Recalculate ANY_JOY_DEVICE_ID and emit signal if something changed\n\t\t\tif changed:\n\t\t\t\t_recalculate_any_joy_button(button)\n\t\t\t\tjoy_button_state_changed.emit()\n\n\t\t# and clear out the pending buttons for this joy\n\t\t_pending_joy_buttons[joy].clear()\t\t\n\n## Processes an input event and updates the state. \nfunc _input(event: InputEvent) -> void:\n\t# print(\"%s - %s\" % [Engine.get_process_frames(), event])\n\t# ----------------------- KEYBOARD -----------------------------\n\tif event is InputEventKey:\n\t\tvar index: int = event.physical_keycode\n\n\t\t# check if the key already changed value this frame\n\t\t# if so, record the change only, it will be applied at the\n\t\t# end of the frame\n\t\tif _pending_keys.has(index):\n\t\t\t_pending_keys[index] = event.pressed\n\t\t\treturn\n\n\t\t_pending_keys[index] = event.pressed\n\n\t\tif event.pressed and not _keys.has(index):\n\t\t\t_keys[index] = true\n\t\t\tkeyboard_state_changed.emit()\n\t\t\treturn\n\n\t\tif not event.pressed and _keys.has(index):\n\t\t\t_keys.erase(index)\n\t\t\tkeyboard_state_changed.emit()\n\t\t\treturn\n\n\t\treturn\n\n\t# ----------------------- MOUSE MOVEMENT -----------------------\n\tif event is InputEventMouseMotion:\n\t\t# Emit the mouse moved signal with the distance moved\n\t\t_mouse_movement += event.relative\n\t\tmouse_position_changed.emit()\n\t\treturn\n\n\t# ----------------------- MOUSE BUTTONS -----------------------\t\t\n\tif event is InputEventMouseButton:\n\t\tvar index: int = event.button_index\n\n\t\t# check if the mouse button already changed value this frame\n\t\t# if so, record the change only, it will be applied at the\n\t\t# end of the frame\n\t\tif _pending_mouse_buttons.has(index):\n\t\t\t_pending_mouse_buttons[index] = event.pressed\n\t\t\treturn\n\n\t\t_pending_mouse_buttons[index] = event.pressed\n\n\t\tif event.pressed and not _mouse_buttons.has(index):\n\t\t\t_mouse_buttons[index] = true\n\t\t\tmouse_button_state_changed.emit()\n\t\t\treturn\n\n\t\tif not event.pressed and _mouse_buttons.has(index):\n\t\t\t_mouse_buttons.erase(index)\n\t\t\tmouse_button_state_changed.emit()\n\t\t\treturn\n\n\t\treturn\n\n\t# ----------------------- JOYSTICK BUTTONS -----------------------\n\tif event is InputEventJoypadButton:\n\t\tvar device_id: int = event.device\n\t\tvar button: int = event.button_index\n\n\t\t# Ignore stray events from disconnected devices\n\t\tif not _joy_buttons.has(device_id):\n\t\t\treturn\n\n\t\tif _pending_joy_buttons[device_id].has(button):\n\t\t\t_pending_joy_buttons[device_id][button] = event.pressed\n\t\t\treturn\n\n\t\t_pending_joy_buttons[device_id][button] = event.pressed\n\n\t\tvar changed:bool = false\n\t\tif event.pressed and not _joy_buttons[device_id].has(button):\n\t\t\t_joy_buttons[device_id][button] = true\n\t\t\tchanged = true\n\t\telif not event.pressed and _joy_buttons[device_id].has(button):\n\t\t\t_joy_buttons[device_id].erase(button)\n\t\t\tchanged = true\n\t\t\n\t\t# Recalculate ANY_JOY_DEVICE_ID and emit signal if something changed\n\t\tif changed:\n\t\t\t_recalculate_any_joy_button(button)\n\t\t\tjoy_button_state_changed.emit()\n\t\treturn\n\n\t# ----------------------- JOYSTICK AXES -----------------------\n\tif event is InputEventJoypadMotion:\n\t\tvar device_id: int = event.device\n\t\tvar axis: int = event.axis\n\n\t\t# Ignore stray events from disconnected devices\n\t\tif not _joy_axes.has(device_id):\n\t\t\treturn\n\n\t\t# update the axis value\n\t\t_joy_axes[device_id][axis] = event.axis_value\n\n\t\t# Recalculate ANY_JOY_DEVICE_ID for this axis and emit signal\n\t\t_recalculate_any_joy_axis(axis)\n\t\tjoy_axis_state_changed.emit()\n\t\treturn\n\n\t# ----------------------- TOUCH INPUT -----------------------\n\n\tif event is InputEventScreenTouch:\n\t\tif event.pressed:\n\t\t\t_finger_positions[event.index] = event.position\n\t\telse:\n\t\t\t_finger_positions.erase(event.index)\n\n\t\ttouch_state_changed.emit()\n\t\treturn\n\n\n\tif event is InputEventScreenDrag:\n\t\t_finger_positions[event.index] = event.position\n\n\t\ttouch_state_changed.emit()\n\t\treturn\n\n\n## Returns true if the key with the given index is currently pressed.\nfunc is_key_pressed(key: Key) -> bool:\n\treturn _keys.has(key)\n\n# Returns true if at least one key in the given array is currently pressed.\nfunc is_at_least_one_key_pressed(keys:Array[Key]) -> bool:\t\n\tfor key in keys:\n\t\tif _keys.has(key):\n\t\t\treturn true\n\treturn false\n\n# Returns true if all keys in the given array are currently pressed.\nfunc are_all_keys_pressed(keys:Array[Key]) -> bool:\n\treturn _keys.has_all(keys)\n\n## Returns true if currently any key is pressed.\nfunc is_any_key_pressed() -> bool:\n\treturn not _keys.is_empty()\n\n## Gets the mouse movement since the last frame.\n## If no movement has been detected, returns Vector2.ZERO.\nfunc get_mouse_delta_since_last_frame() -> Vector2:\n\t# print(\"%s DELTA %s\" % [Engine.get_process_frames(), _mouse_movement])\n\treturn _mouse_movement\n\n## Returns the current mouse position in the root viewport.\nfunc get_mouse_position() -> Vector2:\n\treturn Engine.get_main_loop().root.get_mouse_position()\n\n\n## Returns true if the mouse button with the given index is currently pressed.\t\nfunc is_mouse_button_pressed(button_index: MouseButton) -> bool:\n\treturn _mouse_buttons.has(button_index)\n\n## Returns true if currently any mouse button is pressed.\nfunc is_any_mouse_button_pressed() -> bool:\n\treturn not _mouse_buttons.is_empty()\n\n## Returns the current value of the given joy axis on the device with the given index. If no\n## such device or axis exists, returns 0.0.\nfunc get_joy_axis_value(index:int, axis:JoyAxis) -> float:\n\tvar device_id: int = _joy_index_to_device_id.get(index, -9999)\n\t# unknown device\n\tif device_id == -9999:\n\t\treturn 0.0\n\tif _joy_axes.has(device_id):\n\t\tvar inner = _joy_axes[device_id]\n\t\treturn inner.get(axis, 0.0)\n\treturn 0.0\n\n## Returns true, if the given joy button is currentely pressed on the device with the given index.\nfunc is_joy_button_pressed(index:int, button:JoyButton) -> bool:\t\n\tvar device_id: int = _joy_index_to_device_id.get(index, -9999)\n\t# unknown device\n\tif device_id == -9999:\n\t\treturn false\n\tif _joy_buttons.has(device_id):\n\t\treturn _joy_buttons[device_id].has(button)\n\treturn false\n\n## Returns true, if currently any joy button is pressed on any device.\t\nfunc is_any_joy_button_pressed() -> bool:\n\tfor inner in _joy_buttons.values():\n\t\tif not inner.is_empty():\n\t\t\treturn true\n\treturn false\n\n## Returns true if currently any joy axis is actuated with at least the given strength.\nfunc is_any_joy_axis_actuated(minimum_strength: float) -> bool:\n\tfor inner in _joy_axes.values():\n\t\tfor value in inner.values():\n\t\t\tif abs(value) >= minimum_strength:\n\t\t\t\treturn true\n\treturn false\n\n## Gets the finger position of the finger at the given index.\n## If finger_index is < 0, returns the average of all finger positions.\n## Will only return a position if the amount of fingers\n## currently touching matches finger_count. \n##\n## If no finger position can be determined, returns Vector2.INF.\nfunc get_finger_position(finger_index: int, finger_count: int) -> Vector2:\n\t# if we have no finger positions right now, we can cut it short here\n\tif _finger_positions.is_empty():\n\t\treturn Vector2.INF\n\n\t# If the finger count doesn't match we have no position right now\n\tif _finger_positions.size() != finger_count:\n\t\treturn Vector2.INF\n\n\t# if a finger index is set, use this fingers position, if available\n\tif finger_index > -1:\n\t\treturn _finger_positions.get(finger_index, Vector2.INF)\n\n\tvar result: Vector2 = Vector2.ZERO\n\tfor value in _finger_positions.values():\n\t\tresult += value\n\n\tresult /= float(finger_count)\n\treturn result\n\t\n## Returns the positions of all fingers currently touching.\n## If no finger touches, returns an empty array.\t\nfunc get_finger_positions() -> Array[Vector2]:\n\tvar result:Array[Vector2] = []\n\tresult.assign(_finger_positions.values())\n\treturn result\n\n## Returns true, if currently any finger is touching the screen.\t\nfunc is_any_finger_down() -> bool:\n\treturn not _finger_positions.is_empty()\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_state.gd.uid",
    "content": "uid://c11q8ft87iu87\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_touch_angle.gd",
    "content": "## Input representing angle changes between two fingers. \n@tool\nclass_name GUIDEInputTouchAngle\nextends GUIDEInput\n\n## Unit in which the angle should be provided\nenum AngleUnit {\n\t## Angle is provided in radians\n\tRADIANS = 0,\n\t## Angle is provided in degrees.\n\tDEGREES = 1\n}\n\n## The unit in which the angle should be provided\n@export var unit:AngleUnit = AngleUnit.RADIANS\n\nvar _initial_angle:float = INF\n\n# We use the reset call to calculate the angle for this frame\n# so it can serve as reference for the next frame\nfunc _needs_reset() -> bool:\n\treturn true\n\nfunc _reset() -> void:\n\tvar angle := _calculate_angle()\n\t# update initial angle when input is actuated or stops being actuated\n\tif is_finite(_initial_angle) != is_finite(angle):\n\t\t_initial_angle = angle\n\nfunc _begin_usage() -> void:\n\t# subscribe to relevant input events\n\t_state.touch_state_changed.connect(_refresh)\n\t_refresh()\n\t\nfunc _end_usage() -> void:\n\t# unsubscribe from input events\n\t_state.touch_state_changed.disconnect(_refresh)\n\t\nfunc _refresh():\n\tvar angle := _calculate_angle()\n\t# if either current angle or initial angle is not set,\n\t# we are zero\n\tif not is_finite(angle) or not is_finite(_initial_angle):\n\t\t_value = Vector3.ZERO\n\t\treturn\n\t\t\n\t# we assume that _initial_distance is never 0 because\n\t# you cannot have two fingers physically at the same place\n\t# on a touch screen (unless you're a ghost, which raises\n\t# the question how you are using a touch screen in the first place)\n\tvar final_angle:float = angle - _initial_angle\n\tif unit == AngleUnit.DEGREES:\n\t\tfinal_angle = rad_to_deg(final_angle)\n\t\n\t_value = Vector3(final_angle, 0, 0)\n\t\t\n\t\nfunc _calculate_angle() -> float:\n\tvar pos1:Vector2 = _state.get_finger_position(0, 2)\n\t# if we have no position for first finger, we can immediately abort\n\tif not pos1.is_finite():\n\t\treturn INF\n\t\t\n\tvar pos2:Vector2 = _state.get_finger_position(1, 2)\n\t# if there is no second finger, we can abort as well\n\tif not pos2.is_finite():\n\t\treturn INF\n\t\t\n\t# calculate distance for the fingers\n\treturn -pos1.angle_to_point(pos2)\n\t\n \t\t\nfunc is_same_as(other:GUIDEInput):\n\treturn other is GUIDEInputTouchAngle and \\\n\t\tother.unit == unit\n\n\nfunc _to_string():\n\treturn \"(GUIDEInputTouchAngle unit=\" + (\"radians\" if unit == AngleUnit.RADIANS else \"degrees\") + \")\"\n\n\nfunc _editor_name() -> String:\n\treturn \"Touch Angle\"\n\n\t\nfunc _editor_description() -> String:\n\treturn \"Angle changes of two touching fingers.\"\n\n\nfunc _native_value_type() -> GUIDEAction.GUIDEActionValueType:\n\treturn GUIDEAction.GUIDEActionValueType.AXIS_1D\n\nfunc _device_type() -> DeviceType:\n\treturn DeviceType.TOUCH\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_touch_angle.gd.uid",
    "content": "uid://b3sxmqknm7ljs\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_touch_axis_1d.gd",
    "content": "@tool\nclass_name GUIDEInputTouchAxis1D\nextends GUIDEInputTouchAxisBase\n\nenum GUIDEInputTouchAxis {\n\tX,\n\tY\n}\n\n@export var axis:GUIDEInputTouchAxis:\n\tset(value):\n\t\tif value == axis:\n\t\t\treturn\n\t\taxis = value\n\t\temit_changed()\t\t\n\t\t\nfunc is_same_as(other:GUIDEInput):\n\treturn other is GUIDEInputTouchAxis1D and \\\n\t\tother.finger_count == finger_count and \\\n\t\tother.finger_index == finger_index and \\\n\t\tother.axis == axis\n\nfunc _apply_value(value:Vector2):\n\tmatch axis:\n\t\tGUIDEInputTouchAxis.X:\n\t\t\t_value = Vector3(value.x, 0, 0)\n\t\tGUIDEInputTouchAxis.Y:\n\t\t\t_value = Vector3(value.y, 0, 0)\n\nfunc _to_string():\n\treturn \"(GUIDEInputTouchAxis1D finger_count=\" + str(finger_count) + \\\n\t\t\" finger_index=\" + str(finger_index) +\" axis=\" + (\"X\" if axis == GUIDEInputTouchAxis.X else \"Y\") + \")\"\n\n\nfunc _editor_name() -> String:\n\treturn \"Touch Axis1D\"\n\n\t\nfunc _editor_description() -> String:\n\treturn \"Relative movement of a touching finger on a single axis.\"\n\n\nfunc _native_value_type() -> GUIDEAction.GUIDEActionValueType:\n\treturn GUIDEAction.GUIDEActionValueType.AXIS_1D\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_touch_axis_1d.gd.uid",
    "content": "uid://idi72xetfe0s\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_touch_axis_2d.gd",
    "content": "@tool\nclass_name GUIDEInputTouchAxis2D\nextends GUIDEInputTouchAxisBase\n\nfunc _apply_value(value:Vector2):\n\t_value = Vector3(value.x, value.y, 0)\n\t\t\nfunc is_same_as(other:GUIDEInput):\n\treturn other is GUIDEInputTouchAxis2D and \\\n\t\tother.finger_count == finger_count and \\\n\t\tother.finger_index == finger_index\n\n\nfunc _to_string():\n\treturn \"(GUIDEInputTouchAxis2D finger_count=\" + str(finger_count) + \\\n\t\t\" finger_index=\" + str(finger_index) +\")\"\n\n\nfunc _editor_name() -> String:\n\treturn \"Touch Axis2D\"\n\n\t\nfunc _editor_description() -> String:\n\treturn \"2D relative movement of a touching finger.\"\n\nfunc _native_value_type() -> GUIDEAction.GUIDEActionValueType:\n\treturn GUIDEAction.GUIDEActionValueType.AXIS_2D\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_touch_axis_2d.gd.uid",
    "content": "uid://83ggp3br4dqv\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_touch_axis_base.gd",
    "content": "## Base class for axis-like touch input.\n@tool\nclass_name GUIDEInputTouchAxisBase\nextends GUIDEInputTouchBase\n\nvar _last_position:Vector2 = Vector2.INF\n\n# We use the reset call to calculate the position for this frame\n# so it can serve as reference for the next frame\nfunc _needs_reset() -> bool:\n\treturn true\n\nfunc _reset() -> void:\n\t_last_position = _state.get_finger_position(finger_index, finger_count)\n\t_apply_value(_calculate_value(_last_position))\n\nfunc _begin_usage() -> void:\n\t# subscribe to relevant input events\t\n\t_state.touch_state_changed.connect(_refresh)\n\t_refresh()\n\nfunc _end_usage() -> void:\n\t# unsubscribe from input events\n\t_state.touch_state_changed.disconnect(_refresh)\n\t\nfunc _refresh() -> void:\n\t# calculate live position from the cache\n\tvar new_position:Vector2 = _state.get_finger_position(finger_index, finger_count)\n\n\t_apply_value(_calculate_value(new_position))\n\nfunc _apply_value(value:Vector2):\n\tpass\t\n\t\t\t\nfunc _calculate_value(new_position:Vector2) -> Vector2:\n\t# if we cannot calculate a delta because old or new position\n\t# are undefined, we say the delta is zero\t\n\tif not _last_position.is_finite() or not new_position.is_finite():\n\t\treturn Vector2.ZERO\n\t\n\treturn new_position - _last_position\n\n\t\t\nfunc is_same_as(other:GUIDEInput):\n\treturn other is GUIDEInputTouchAxis2D and \\\n\t\tother.finger_count == finger_count and \\\n\t\tother.finger_index == finger_index\n\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_touch_axis_base.gd.uid",
    "content": "uid://c32np6tk2l12\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_touch_base.gd",
    "content": "## Base class for generic touch input\n@tool\nclass_name GUIDEInputTouchBase\nextends GUIDEInput\n\n## The number of fingers to be tracked.\n@export_range(1, 5, 1, \"or_greater\") var finger_count:int = 1:\n\tset(value):\n\t\tif value < 1:\n\t\t\tvalue = 1\n\t\tfinger_count = value\n\t\temit_changed()\n\n## The index of the finger for which the position/delta should be reported \n## (0 = first finger, 1 = second finger, etc.). If -1, reports the average position/delta for \n## all fingers currently touching.\n@export_range(-1, 4, 1, \"or_greater\") var finger_index:int = 0:\n\tset(value):\n\t\tif value < -1:\n\t\t\tvalue = -1\n\t\tfinger_index = value\n\t\temit_changed()\n\n\nfunc _device_type() -> DeviceType:\n\treturn DeviceType.TOUCH\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_touch_base.gd.uid",
    "content": "uid://dfmq3cijb3ju3\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_touch_distance.gd",
    "content": "## Input representing the distance changes between two fingers. \n@tool\nclass_name GUIDEInputTouchDistance\nextends GUIDEInput\n\nvar _initial_distance:float = INF\n\n# We use the reset call to calculate the distance for this frame\n# so it can serve as reference for the next frame\nfunc _needs_reset() -> bool:\n\treturn true\n\nfunc _reset() -> void:\n\tvar distance := _calculate_distance()\n\t# update initial distance when input is actuated or stops being actuated\n\tif is_finite(_initial_distance) != is_finite(distance):\n\t\t_initial_distance = distance\n\t\t\n\nfunc _begin_usage() -> void:\n\t# subscribe to relevant input events\n\t_state.touch_state_changed.connect(_refresh)\n\t_refresh()\n\t\nfunc _end_usage() -> void:\n\t# unsubscribe from input events\n\t_state.touch_state_changed.disconnect(_refresh)\n\t\n\nfunc _refresh() -> void:\n\tvar distance := _calculate_distance()\n\t# if either current distance or initial distance is not set,\n\t# we are zero\n\tif not is_finite(distance) or not is_finite(_initial_distance):\n\t\t_value = Vector3.ZERO\n\t\treturn\n\t\t\n\t# we assume that _initial_distance is never 0 because\n\t# you cannot have two fingers physically at the same place\n\t# on a touch screen\n\t_value = Vector3(distance / _initial_distance, 0, 0)\n\t\t\n\t\nfunc _calculate_distance() -> float:\n\tvar pos1:Vector2 = _state.get_finger_position(0, 2)\n\t# if we have no position for first finger, we can immediately abort\n\tif not pos1.is_finite():\n\t\treturn INF\n\t\t\n\tvar pos2:Vector2 = _state.get_finger_position(1, 2)\n\t# if there is no second finger, we can abort as well\n\tif not pos2.is_finite():\n\t\treturn INF\n\t\t\n\t# calculate distance for the fingers\n\treturn pos1.distance_to(pos2)\n\t\n\nfunc is_same_as(other:GUIDEInput) -> bool:\n\treturn other is GUIDEInputTouchDistance\n\n\nfunc _to_string() -> String:\n\treturn \"(GUIDEInputTouchDistance)\"\n\n\nfunc _editor_name() -> String:\n\treturn \"Touch Distance\"\n\n\t\nfunc _editor_description() -> String:\n\treturn \"Distance of two touching fingers.\"\n\n\nfunc _native_value_type() -> GUIDEAction.GUIDEActionValueType:\n\treturn GUIDEAction.GUIDEActionValueType.AXIS_1D\n\n\nfunc _device_type() -> DeviceType:\n\treturn DeviceType.TOUCH\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_touch_distance.gd.uid",
    "content": "uid://hjbdbq1wud8h\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_touch_position.gd",
    "content": "@tool\nclass_name GUIDEInputTouchPosition\nextends GUIDEInputTouchBase\n\n\nfunc _begin_usage():\n\t# subscribe to touch events\n\t_state.touch_state_changed.connect(_refresh)\n\t_refresh()\n\t\nfunc _end_usage():\n\t# unsubscribe from touch events\n\t_state.touch_state_changed.disconnect(_refresh)\n\nfunc _refresh() -> void:\n\t# update finger position\n\tvar position:Vector2 = _state.get_finger_position(finger_index, finger_count)\n\tif not position.is_finite():\n\t\t_value = Vector3.INF\n\t\treturn\n\t\t\n\t_value = Vector3(position.x, position.y, 0) \n\n\t\t\nfunc is_same_as(other:GUIDEInput):\n\treturn other is GUIDEInputTouchPosition and \\\n\t\tother.finger_count == finger_count and \\\n\t\tother.finger_index == finger_index\n\n\nfunc _to_string():\n\treturn \"(GUIDEInputTouchPosition finger_count=\" + str(finger_count) + \\\n\t\t\" finger_index=\" + str(finger_index) +\")\"\n\n\nfunc _editor_name() -> String:\n\treturn \"Touch Position\"\n\n\t\nfunc _editor_description() -> String:\n\treturn \"Position of a touching finger.\"\n\n\nfunc _native_value_type() -> GUIDEAction.GUIDEActionValueType:\n\treturn GUIDEAction.GUIDEActionValueType.AXIS_2D\n\n"
  },
  {
    "path": "addons/guide/inputs/guide_input_touch_position.gd.uid",
    "content": "uid://c2bvqibcqlmv5\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier.gd",
    "content": "@tool\n@icon(\"res://addons/guide/modifiers/guide_modifier.svg\")\nclass_name GUIDEModifier\nextends Resource\n\n## Returns whether this modifier is the same as the other modifier.\n## This is used to determine if a modifier can be reused during context switching.\nfunc is_same_as(other:GUIDEModifier) -> bool:\n\treturn self == other\n\n## Called when the modifier is started to be used by GUIDE. Can be used to perform\n## initializations.\nfunc _begin_usage() -> void :\n\tpass\n\t\n## Called, when the modifier is no longer used by GUIDE. Can be used to perform\n## cleanup.\nfunc _end_usage() -> void:\n\tpass\n\n## Called to modify the input value before it is passed to the triggers.\nfunc _modify_input(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> Vector3:\n\treturn input\n\n## The name as it should be displayed in the editor.\nfunc _editor_name() -> String:\n\treturn \"\"\n\n## The description as it should be displayed in the editor.\nfunc _editor_description() -> String:\n\treturn \"\"\n\n## Whether this modifier needs physics processing. This is queried once\n## when the modifier is used, not every frame.\nfunc _needs_physics_process() -> bool:\n\treturn false\n\n## Called to update any internal state of the modifier during physics processing.\n## Only called if _needs_physics_process() returns true.\nfunc _physics_process(_delta: float) -> void:\n\tpass\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier.gd.uid",
    "content": "uid://bl8rjl4oaldje\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://j64d8n4am2uh\"\npath=\"res://.godot/imported/guide_modifier.svg-8cf939ca3244410aba00f7b558561d72.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/modifiers/guide_modifier.svg\"\ndest_files=[\"res://.godot/imported/guide_modifier.svg-8cf939ca3244410aba00f7b558561d72.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=0.5\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_3d_coordinates.gd",
    "content": "## Converts a position input in viewport coordinates (e.g. from the mouse position input)\n## into 3D coordinates (e.g. 3D world coordinates). Useful to get a 3D 'world' position.\n## Returns a Vector3.INF if no 3D world coordinates can be determined.\n@tool\nclass_name GUIDEModifier3DCoordinates\nextends GUIDEModifier\n\n## The maximum depth of the ray cast used to detect the 3D position.\n@export var max_depth:float = 1000.0\n\n## Whether the rays cast should collide with areas.\n@export var collide_with_areas:bool = false\n\n## Collision mask to use for the ray cast.\n@export_flags_3d_physics var collision_mask:int\n\n# The latest known input for this physics frame\nvar _input: Vector3 = Vector3.ZERO\n# The output coordinates matching the latest known input\nvar _latest_update_input: Vector3 = Vector3.ZERO\n\nfunc is_same_as(other:GUIDEModifier) -> bool:\n\treturn other is GUIDEModifier3DCoordinates and \\\n\t\tcollide_with_areas == other.collide_with_areas and \\\n\t\tcollision_mask == other.collision_mask and \\\n\t\tis_equal_approx(max_depth, other.max_depth)\n\nfunc _needs_physics_process() -> bool:\n\treturn true\n\nfunc _physics_process(_delta: float) -> void:\n\t_latest_update_input = _update_input(_input)\n\n\nfunc _modify_input(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> Vector3:\n\t_input = input\n\treturn _latest_update_input\n\n\nfunc _update_input(input: Vector3) -> Vector3:\n\t# if we collide with nothing, no need to even try\n\tif collision_mask == 0:\n\t\treturn Vector3.INF\n\t\t\n\tif not input.is_finite():\n\t\treturn Vector3.INF\n\t\t\t\n\tvar viewport = Engine.get_main_loop().root\n\tvar camera:Camera3D = viewport.get_camera_3d()\n\tif camera == null:\n\t\treturn Vector3.INF\n\t\t\n\tvar input_position:Vector2 = Vector2(input.x, input.y)\t\n\t\t\n\tvar from:Vector3 = camera.project_ray_origin(input_position)\n\tvar to:Vector3 = from + camera.project_ray_normal(input_position) * max_depth\n\tvar query:= PhysicsRayQueryParameters3D.create(from, to, collision_mask)\n\tquery.collide_with_areas = collide_with_areas\n\t\t\n\tvar result = viewport.world_3d.direct_space_state.intersect_ray(query)\n\tif result.has(\"position\"):\n\t\treturn result.position\n\t\t\n\treturn Vector3.INF\t\n\nfunc _editor_name() -> String:\n\treturn \"3D coordinates\"\n\n\nfunc _editor_description() -> String:\n\treturn \"Converts a position input in viewport coordinates (e.g. from the mouse position input)\\n\" + \\\n\t\t\"into 3D coordinates (e.g. 3D world coordinates). Useful to get a 3D 'world' position.\"\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_3d_coordinates.gd.uid",
    "content": "uid://cw8qjwdktercg\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_8_way_direction.gd",
    "content": "@tool\n## A filter that converts a 2D input into a boolean that is true when the \n## input direction matches the selected direction. Note, that north is negative Y, \n## because in Godot negative Y is up.\nclass_name GUIDEModifier8WayDirection\nextends GUIDEModifier\n\nenum GUIDEDirection {\n \tEAST = 0, \n\tNORTH_EAST = 1,\n\tNORTH = 2, \n\tNORTH_WEST = 3,\n\tWEST = 4, \n\tSOUTH_WEST = 5,\n\tSOUTH = 6, \n\tSOUTH_EAST = 7\n}\n\n## The direction in which the input should point.\n@export var direction:GUIDEDirection = GUIDEDirection.EAST\n\nfunc is_same_as(other:GUIDEModifier) -> bool:\n\treturn other is GUIDEModifier8WayDirection and \\\n\t\tdirection == other.direction\n\nfunc _modify_input(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> Vector3:\n\tif not input.is_finite():\n\t\treturn Vector3.INF\n\t\t\n\tif input.is_zero_approx():\n\t\treturn Vector3.ZERO\n\t\t\n\t\n\t\t\n\t# get the angle in which the direction is pointing in radians.\n\tvar angle_radians := atan2( -input.y, input.x );\n\tvar octant := roundi( 8 * angle_radians / TAU + 8 ) % 8;\n\tif octant == direction:\n\t\treturn Vector3.RIGHT # (1, 0, 0) indicating boolean true\n\telse:\n\t\treturn Vector3.ZERO\n\n\t\t\t\nfunc _editor_name() -> String:\n\treturn \"8-way direction\"\n\n\nfunc _editor_description() -> String:\n\treturn \"Converts a 2D input into a boolean that is true when the\\n\" + \\\n\t\t\"input direction matches the selected direction. Note, that north is negative Y,\\n\" + \\\n\t\t\"because in Godot negative Y is up.\"\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_8_way_direction.gd.uid",
    "content": "uid://cmjelxqb3e7n6\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_canvas_coordinates.gd",
    "content": "## Converts a position input in viewport coordinates (e.g. from the mouse position input)\n## into canvas coordinates (e.g. 2D world coordinates). Useful to get a 2D 'world' position.\n@tool\nclass_name GUIDEModifierCanvasCoordinates\nextends GUIDEModifier\n\n## If checked, the input will be treated as relative input (position change)\n## rather than absolute input (position).\n@export var relative_input:bool:\n\tset(value):\n\t\trelative_input = value\n\t\temit_changed()\n\nfunc is_same_as(other:GUIDEModifier) -> bool:\n\treturn other is GUIDEModifierCanvasCoordinates and \\\n\t\trelative_input == other.relative_input\n\nfunc _modify_input(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> Vector3:\n\tif not input.is_finite():\n\t\treturn Vector3.INF\n\t\t\n\tvar viewport = Engine.get_main_loop().root\n\tvar transform = viewport.canvas_transform.affine_inverse()\n\tvar coordinates = transform * Vector2(input.x, input.y)\n\t\n\tif relative_input:\n\t\tvar origin = transform * Vector2.ZERO\n\t\tcoordinates -= origin\n\t \n\treturn Vector3(coordinates.x, coordinates.y, input.z)\n\n\nfunc _editor_name() -> String:\n\treturn \"Canvas coordinates\"\n\n\nfunc _editor_description() -> String:\n\treturn \"Converts a position input in viewport coordinates (e.g. from the mouse position input)\\n\" + \\\n\t\t\"into canvas coordinates (e.g. 2D world coordinates). Useful to get a 2D 'world' position.\"\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_canvas_coordinates.gd.uid",
    "content": "uid://vho2v7ax07ef\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_curve.gd",
    "content": "@tool\n## Applies a separate curve to each input axis.\nclass_name GUIDEModifierCurve\nextends GUIDEModifier\n\n\n## The curve to apply to the x axis\n@export var curve: Curve = default_curve()\n\n## Apply modifier to X axis\n@export var x: bool = true\n\n## Apply modifier to Y axis\n@export var y: bool = true\n\n## Apply modifier to Z axis\n@export var z: bool = true\n\n\n## Create default curve resource with a smoothstep, 0.0 - 1.0 input/output range\nstatic func default_curve() -> Curve:\n\tvar curve := Curve.new()\n\tcurve.add_point(Vector2(0.0, 0.0))\n\tcurve.add_point(Vector2(1.0, 1.0))\n\n\treturn curve\n\n\t\nfunc is_same_as(other: GUIDEModifier) -> bool:\n\treturn other is GUIDEModifierCurve and \\\n\t\tcurve == other.curve and \\\n\t\tx == other.x and \\\n\t\ty == other.y and \\\n\t\tz == other.z\n\nfunc _modify_input(input: Vector3, delta: float, value_type: GUIDEAction.GUIDEActionValueType) -> Vector3:\n\t# Curve should never be null\n\tif curve == null:\n\t\tpush_error(\"No curve added to Curve modifier.\")\n\t\treturn input\n\n\tif not input.is_finite():\n\t\treturn Vector3.INF\n\n\t# Return vector with enabled axes modified, others remain unchanged.\n\treturn Vector3(\n\t\tcurve.sample(input.x) if x else input.x,\n\t\tcurve.sample(input.y) if y else input.y,\n\t\tcurve.sample(input.z) if z else input.z\n\t)\n\n\nfunc _editor_name() -> String:\n\treturn \"Curve\"\n\n\nfunc _editor_description() -> String:\n\treturn \"Applies a curve to each input axis.\"\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_curve.gd.uid",
    "content": "uid://trjb6t778n84\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_deadzone.gd",
    "content": "@tool\n## Inputs between the lower and upper threshold are mapped 0 -> 1.\n## Values outside the thresholds are clamped.\nclass_name GUIDEModifierDeadzone\nextends GUIDEModifier\n\n## Lower threshold for the deadzone. \n@export_range(0, 1) var lower_threshold: float = 0.2:\n\tset(value):\n\t\tif value > upper_threshold:\n\t\t\tlower_threshold = upper_threshold\n\t\telse:\n\t\t\tlower_threshold = value\n\t\temit_changed()\n\n## Upper threshold for the deadzone.\n@export_range(0, 1) var upper_threshold: float = 1.0:\n\tset(value):\n\t\tif value < lower_threshold:\n\t\t\tupper_threshold = lower_threshold\n\t\telse:\n\t\t\tupper_threshold = value\n\t\temit_changed()\n\n\nfunc is_same_as(other: GUIDEModifier) -> bool:\n\treturn other is GUIDEModifierDeadzone and \\\n\tis_equal_approx(lower_threshold, other.lower_threshold) and \\\n\tis_equal_approx(upper_threshold, other.upper_threshold)\n\n\nfunc _rescale(value: float) -> float:\n\treturn min(1.0, (max(0.0, abs(value) - lower_threshold) / (upper_threshold - lower_threshold))) * sign(value)\n\n\nfunc _modify_input(input: Vector3, _delta: float, value_type: GUIDEAction.GUIDEActionValueType) -> Vector3:\n\tif upper_threshold <= lower_threshold:\n\t\treturn input\n\t\n\tif not input.is_finite():\n\t\treturn Vector3.INF\n\t\n\tmatch value_type:\n\t\tGUIDEAction.GUIDEActionValueType.BOOL, GUIDEAction.GUIDEActionValueType.AXIS_1D:\n\t\t\treturn Vector3(_rescale(input.x), input.y, input.z)\n\t\t\n\t\tGUIDEAction.GUIDEActionValueType.AXIS_2D:\n\t\t\tvar v2d := Vector2(input.x, input.y)\n\t\t\tif v2d.is_zero_approx():\n\t\t\t\treturn Vector3(0, 0, input.z)\n\t\t\tv2d = v2d.normalized() * _rescale(v2d.length())\n\t\t\treturn Vector3(v2d.x, v2d.y, input.z)\n\t\t\n\t\tGUIDEAction.GUIDEActionValueType.AXIS_3D:\n\t\t\tif input.is_zero_approx():\n\t\t\t\treturn Vector3.ZERO\n\t\t\treturn input.normalized() * _rescale(input.length())\n\t\t_:\n\t\t\tpush_error(\"Unsupported value type. This is a bug. Please report it.\")\n\t\t\treturn input\n\n\nfunc _editor_name() -> String:\n\treturn \"Deadzone\"\n\n\nfunc _editor_description() -> String:\n\treturn \"Inputs between the lower and upper threshold are mapped 0 -> 1.\\n\" + \\\n\t\"Values outside the thresholds are clamped.\"\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_deadzone.gd.uid",
    "content": "uid://c47lkb48itd6l\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_input_swizzle.gd",
    "content": "## Swizzle the input vector components. Useful to map 1D input to 2D or vice versa.\n@tool\nclass_name GUIDEModifierInputSwizzle\nextends GUIDEModifier\n\nenum GUIDEInputSwizzleOperation {\n\t## Swap X and Y axes.\n\tYXZ,\n\t## Swap X and Z axes.\n\tZYX,\n\t## Swap Y and Z axes.\n\tXZY,\n\t## Y to X, Z to Y, X to Z.\n\tYZX,\n\t## Y to Z, Z to X, X to Y.\n\tZXY\n}\n\n## The new order into which the input should be brought.\n@export var order:GUIDEInputSwizzleOperation = GUIDEInputSwizzleOperation.YXZ\n\nfunc is_same_as(other:GUIDEModifier) -> bool:\n\treturn other is GUIDEModifierInputSwizzle and \\\n\t\torder == other.order\n\nfunc _modify_input(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> Vector3:\n\tmatch order:\n\t\tGUIDEInputSwizzleOperation.YXZ:\n\t\t\treturn Vector3(input.y, input.x, input.z)\n\t\tGUIDEInputSwizzleOperation.ZYX:\n\t\t\treturn Vector3(input.z, input.y, input.x)\n\t\tGUIDEInputSwizzleOperation.XZY:\n\t\t\treturn Vector3(input.x, input.z, input.y)\n\t\tGUIDEInputSwizzleOperation.YZX:\n\t\t\treturn Vector3(input.y, input.z, input.x)\n\t\tGUIDEInputSwizzleOperation.ZXY:\n\t\t\treturn Vector3(input.z, input.x, input.y)\n\t\t_:\n\t\t\tpush_error(\"Unknown order \", order , \" this is most likely a bug, please report it.\")\n\t\t\treturn input\n\t\t\t\nfunc _editor_name() -> String:\n\treturn \"Input Swizzle\"\t\t\t\t\n\t\t\t\nfunc _editor_description() -> String:\n\treturn \"Swizzle the input vector components. Useful to map 1D input to 2D or vice versa.\"\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_input_swizzle.gd.uid",
    "content": "uid://bm5gjgadon6hb\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_magnitude.gd",
    "content": "## Returns the magnitude of the input value.\n@tool\nclass_name GUIDEModifierMagnitude\nextends GUIDEModifier\n\nfunc is_same_as(other:GUIDEModifier) -> bool:\n\treturn other is GUIDEModifierMagnitude\n\nfunc _modify_input(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> Vector3:\n\tif not input.is_finite():\n\t\treturn Vector3.INF\n\t\t\n\treturn Vector3(input.length(), 0, 0)\n\nfunc _editor_name() -> String:\n\treturn \"Magnitude\"\t\n\n\nfunc _editor_description() -> String:\n\treturn \"Returns the magnitude of the input vector.\"\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_magnitude.gd.uid",
    "content": "uid://bx1tx0u2d1crk\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_map_range.gd",
    "content": "@tool\n## Maps an input range to an output range and optionally clamps the output.\nclass_name GUIDEModifierMapRange\nextends GUIDEModifier\n\n## Should the output be clamped to the range?\n@export var apply_clamp:bool = true\n\n## The minimum input value\n@export var input_min:float = 0.0\n\n## The maximum input value\n@export var input_max:float = 1.0\n\n## The minimum output value\n@export var output_min:float = 0.0\n\n## The maximum output value\n@export var output_max:float = 1.0\n\n## Apply modifier to X axis\n@export var x:bool = true\n\n## Apply modifier to Y axis\n@export var y:bool = true\n\n## Apply modifier to Z axis\n@export var z:bool = true\n\nvar _omin:float\nvar _omax:float\n\nfunc is_same_as(other:GUIDEModifier) -> bool:\n\treturn other is GUIDEModifierMapRange and \\\n\t\tapply_clamp == other.apply_clamp and \\\n\t\tx == other.x and \\\n\t\ty == other.y and \\\n\t\tz == other.z and \\\n\t\tis_equal_approx(input_min, other.input_min) and \\\n\t\tis_equal_approx(input_max, other.input_max) and \\\n\t\tis_equal_approx(output_min, other.output_min) and \\\n\t\tis_equal_approx(output_max, other.output_max)\n\nfunc _begin_usage():\n\t# we calculate the min and max of the output range here, so we can use them later and don't have to\n\t# recalculate them every time the modifier is used\n\t_omin = min(output_min, output_max)\n\t_omax = max(output_min, output_max)\n\nfunc _modify_input(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> Vector3:\n\tif not input.is_finite():\n\t\treturn Vector3.INF\n\t\t\n\tvar x_value:float = remap(input.x, input_min, input_max, output_min, output_max)\n\tvar y_value:float = remap(input.y, input_min, input_max, output_min, output_max)\n\tvar z_value:float = remap(input.z, input_min, input_max, output_min, output_max)\n\t\n\tif apply_clamp:\n\t\t# clamp doesn't handle reverse ranges, so we need to use our calculated normalized output range\n\t\t# to clamp the output values\n\t\tx_value = clamp(x_value, _omin, _omax)\n\t\ty_value = clamp(y_value, _omin, _omax)\n\t\tz_value = clamp(z_value, _omin, _omax)\n\n\t# Return vector with enabled axes set, others unchanged\n\treturn Vector3(\n\t\tx_value if x else input.x,\n\t\ty_value if y else input.y,\n\t\tz_value if z else input.z,\n\t)\n\n\nfunc _editor_name() -> String:\n\treturn \"Map Range\"\n\n\nfunc _editor_description() -> String:\n\treturn \"Maps an input range to an output range and optionally clamps the output\"\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_map_range.gd.uid",
    "content": "uid://cs70d8i3sqe7p\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_negate.gd",
    "content": "## Inverts input per axis.\n@tool\nclass_name GUIDEModifierNegate\nextends GUIDEModifier\n\n## Whether the X axis should be inverted.\n@export var x:bool = true:\n\tset(value):\n\t\tif x == value:\n\t\t\treturn\n\t\tx = value\n\t\t_update_caches()\n\t\temit_changed()\n\t\t\n## Whether the Y axis should be inverted.\t\t\n@export var y:bool = true:\n\tset(value):\n\t\tif y == value:\n\t\t\treturn\n\t\ty = value\n\t\t_update_caches()\n\t\temit_changed()\n\n## Whether the Z axis should be inverted.\n@export var z:bool = true:\n\tset(value):\n\t\tif z == value:\n\t\t\treturn\n\t\tz = value\n\t\t_update_caches()\n\t\temit_changed()\n\nvar _multiplier:Vector3 = Vector3.ONE * -1\n\nfunc is_same_as(other:GUIDEModifier) -> bool:\n\treturn other is GUIDEModifierNegate and \\\n\t\tx == other.x and \\\n\t\ty == other.y and \\\n\t\tz == other.z\n\nfunc _update_caches():\n\t_multiplier.x = -1 if x else 1\n\t_multiplier.y = -1 if y else 1\n\t_multiplier.z = -1 if z else 1\n\t\t\n\nfunc _modify_input(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> Vector3:\n\tif not input.is_finite():\n\t\treturn Vector3.INF\n\t\t\n\treturn input * _multiplier\n\nfunc _editor_name() -> String:\n\treturn \"Negate\"\t\n\n\nfunc _editor_description() -> String:\n\treturn \"Inverts input per axis.\"\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_negate.gd.uid",
    "content": "uid://ckggy40lm0vjc\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_normalize.gd",
    "content": "## Normalizes the input vector.\n@tool\nclass_name GUIDEModifierNormalize\nextends GUIDEModifier\n\nfunc is_same_as(other:GUIDEModifier) -> bool:\n\treturn other is GUIDEModifierNormalize\n\nfunc _modify_input(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> Vector3:\n\tif not input.is_finite():\n\t\treturn Vector3.INF\n\t\t\n\treturn input.normalized()\n\nfunc _editor_name() -> String:\n\treturn \"Normalize\"\t\n\n\nfunc _editor_description() -> String:\n\treturn \"Normalizes the input vector.\"\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_normalize.gd.uid",
    "content": "uid://bs86pentlbwbi\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_positive_negative.gd",
    "content": "## Limits inputs to positive or negative values.\n@tool\nclass_name GUIDEModifierPositiveNegative\nextends GUIDEModifier\n\nenum LimitRange {\n\tPOSITIVE = 1,\n\tNEGATIVE = 2\n}\n\n## The range of numbers to which the input should be limited\n@export var range:LimitRange = LimitRange.POSITIVE\n\n## Whether the X axis should be affected.\n@export var x:bool = true:\n\tset(value):\n\t\tif x == value:\n\t\t\treturn\n\t\tx = value\n\t\temit_changed()\n\t\t\n## Whether the Y axis should be affected.\n@export var y:bool = true:\n\tset(value):\n\t\tif y == value:\n\t\t\treturn\n\t\ty = value\n\t\temit_changed()\n\n## Whether the Z axis should be affected.\n@export var z:bool = true:\n\tset(value):\n\t\tif z == value:\n\t\t\treturn\n\t\tz = value\n\t\temit_changed()\n\n\nfunc is_same_as(other:GUIDEModifier) -> bool:\n\treturn other is GUIDEModifierPositiveNegative and \\\n\t\trange == other.range and \\\n\t\tx == other.x and \\\n\t\ty == other.y and \\\n\t\tz == other.z\n\n\nfunc _modify_input(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> Vector3:\n\tif not input.is_finite():\n\t\treturn Vector3.INF\n\t\t\n\tmatch range:\n\t\tLimitRange.POSITIVE:\n\t\t\treturn Vector3(\n\t\t\t\tmax(0, input.x) if x else input.x, \\\n\t\t\t\tmax(0, input.y) if y else input.y, \\\n\t\t\t\tmax(0, input.z) if z else input.z  \\\n\t\t\t)\n\t\tLimitRange.NEGATIVE:\n\t\t\treturn Vector3(\n\t\t\t\tmin(0, input.x) if x else input.x, \\\n\t\t\t\tmin(0, input.y) if y else input.y, \\\n\t\t\t\tmin(0, input.z) if z else input.z  \\\n\t\t\t)\n\t# should never happen\n\treturn input\n\nfunc _editor_name() -> String:\n\treturn \"Positive/Negative\"\t\n\n\nfunc _editor_description() -> String:\n\treturn \"Clamps the input to positive or negative values.\"\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_positive_negative.gd.uid",
    "content": "uid://cbe2gpphnd1fg\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_scale.gd",
    "content": "@tool\n## Scales the input by the given value and optionally, delta time.\nclass_name GUIDEModifierScale\nextends GUIDEModifier\n\n## The scale by which the input should be scaled.\n@export var scale:Vector3 = Vector3.ONE:\n\tset(value):\n\t\tscale = value\n\t\temit_changed()\n\t\t\n\t\t\n## If true, delta time will be multiplied in addition to the scale.\n@export var apply_delta_time:bool = false:\n\tset(value):\n\t\tapply_delta_time = value\n\t\temit_changed()\n\nfunc is_same_as(other:GUIDEModifier) -> bool:\n\treturn other is GUIDEModifierScale and \\\n\t\tapply_delta_time == other.apply_delta_time and \\\n\t\tscale.is_equal_approx(other.scale)\n\nfunc _modify_input(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> Vector3:\n\tif not input.is_finite():\n\t\treturn Vector3.INF\n\t\t\n\tif apply_delta_time:\n\t\treturn input * scale * delta\n\telse:\n\t\treturn input * scale\n\n\nfunc _editor_name() -> String:\n\treturn \"Scale\"\n\n\nfunc _editor_description() -> String:\n\treturn \"Scales the input by the given value and optionally, delta time.\"\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_scale.gd.uid",
    "content": "uid://bjm4myqxg4phm\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_virtual_cursor.gd",
    "content": "## Stateful modifier which provides a virtual \"mouse\" cursor driven by input. The modifier\n## returns the current cursor position in pixels releative to the origin of the currently\n## active window. \n@tool\nclass_name GUIDEModifierVirtualCursor\nextends GUIDEModifier\n\nenum ScreenScale {\n\t## Input is not scaled with input screen size. This means that the cursor will\n\t## visually move slower on higher resolutions.\n\tNONE = 0,\n\t## Input is scaled with the longer axis of the screen size (e.g. width in\n\t## landscape mode, height in portrait mode). The cursor will move with\n\t## the same visual speed on all resolutions.\n\tLONGER_AXIS = 1,\n\t## Input is scaled with the shorter axis of the screen size (e.g. height in\n\t## landscape mode, width in portrait mode). The cursor will move with the \n\t## same visual speed on all resolutions.\n\tSHORTER_AXIS = 2\n}\n\n## The initial position of the virtual cursor (given in screen relative coordinates)\n@export var initial_position:Vector2 = Vector2(0.5, 0.5):\n\tset(value):\n\t\tinitial_position = value.clamp(Vector2.ZERO, Vector2.ONE)\n\n## Whether the initial position should be taken from the current mouse\n## position. If true, this has precedence over the initial_position setting.\n@export var initialize_from_mouse_position:bool = false\n\n## Whether the virtual cursor's position should be applied to the \n## mouse position when this modifier is deactivated.\n@export var apply_to_mouse_position_on_deactivation:bool = false\n\n## The cursor movement speed in pixels.\n@export var speed:Vector3 = Vector3.ONE\n\n## Screen scaling to be applied to the cursor movement. This controls\n## whether the cursor movement speed is resolution dependent or not.\n## If set to anything but [code]None[/code] then the input value will\n## be multiplied with the window width/height depending on the setting.\n@export var screen_scale:ScreenScale = ScreenScale.LONGER_AXIS\n\n\n## The scale by which the input should be scaled.\n## @deprecated: use [member speed] instead. \nvar scale:Vector3:\n\tget: return speed\n\tset(value): speed = value\n\n## If true, the cursor movement speed is in pixels per second, otherwise it is in pixels\n## per frame.\n@export var apply_delta_time:bool = true\n\n\n## Cursor offset in pixels.\nvar _offset:Vector3 = Vector3.ZERO\n\nfunc is_same_as(other:GUIDEModifier) -> bool:\n\treturn other is GUIDEModifierVirtualCursor and \\\n\t\tscreen_scale == other.screen_scale and \\\n\t\tapply_delta_time == other.apply_delta_time and \\\n\t\tinitial_position.is_equal_approx(other.initial_position) and \\\n\t\tspeed.is_equal_approx(other.speed)\n\n## Returns the scaled screen size. This takes Godot's scaling factor for windows into account.\nfunc _get_scaled_screen_size():\n\t# Get window size, including scaling factor\n\tvar window = Engine.get_main_loop().get_root()\n\treturn window.get_screen_transform().affine_inverse() * Vector2(window.size)\n\nfunc _begin_usage():\n\tvar window_size = _get_scaled_screen_size()\n\tif initialize_from_mouse_position:\n\t\tif Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:\n\t\t\tpush_warning(\"Mouse mode is captured. In this mode the mouse cursor is fixed to center of the screen. Use one of the other mouse modes instead.\")\n\n\t\tvar window:Window = Engine.get_main_loop().get_root()\n\t\tvar mouse_position := window.get_mouse_position()\n\t\t_offset = Vector3(mouse_position.x, mouse_position.y, 0)\n\telse:\n\t\t_offset = Vector3(window_size.x * initial_position.x, window_size.y * initial_position.y, 0)\n\n\nfunc _end_usage():\n\tif apply_to_mouse_position_on_deactivation:\n\t\tif Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:\n\t\t\tpush_warning(\"Mouse mode is captured. In this mode the mouse cursor cannot be moved. Use one of the other mouse modes instead.\")\n\t\t\treturn\n\t\t\t\n\t\tvar window:Window = Engine.get_main_loop().get_root()\n\t\tvar mouse_position := window.get_mouse_position()\n\t\tvar difference := Vector2(_offset.x, _offset.y) - mouse_position\n\t\twindow.warp_mouse(Vector2(_offset.x, _offset.y))\n\t\t# also issue a mouse motion event to GUIDE, because godot's built-in input\n\t\t# will only notice the movement next frame and then our internal state\n\t\t# is off.\n\t\tvar motion_event := InputEventMouseMotion.new()\n\t\tmotion_event.relative = difference\n\t\tGUIDE.inject_input(motion_event)\n\t\t\n\nfunc _modify_input(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> Vector3:\n\tif not input.is_finite():\n\t\t# input is invalid, so just return current cursor position\n\t\treturn _offset\n\t\t\n\tvar window_size = _get_scaled_screen_size() \n\tinput *= speed\n\t\t\n\tif apply_delta_time:\n\t\tinput *= delta\n\n\tvar screen_scale_factor:float = 1.0\n\tmatch screen_scale:\n\t\tScreenScale.LONGER_AXIS:\n\t\t\tscreen_scale_factor = max(window_size.x, window_size.y)\n\t\tScreenScale.SHORTER_AXIS:\n\t\t\tscreen_scale_factor = min(window_size.x, window_size.y)\t\n\t\t\n\tinput *= screen_scale_factor\n\t\t\n\t# apply input and clamp to window size\t\n\t_offset = (_offset + input).clamp(Vector3.ZERO, Vector3(window_size.x, window_size.y, 0))\n\t\n\treturn _offset\n\nfunc _editor_name() -> String:\n\treturn \"Virtual Cursor\"\n\n\nfunc _editor_description() -> String:\n\treturn \"Stateful modifier which provides a virtual \\\"mouse\\\" cursor driven by input. The modifier\\n\" + \\\n\t\t\t\"returns the current cursor position in pixels releative to the origin of the currently \\n\" + \\\n\t\t\t\"active window.\"\n\n\n# support for legacy properties\nfunc _get_property_list():\n\treturn [\n\t\t{\n\t\t\t\"name\": \"scale\",\n\t\t\t\"type\": TYPE_VECTOR3,\n\t\t\t\"usage\": PROPERTY_USAGE_NO_EDITOR\n\t\t}\n\t]\n\t\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_virtual_cursor.gd.uid",
    "content": "uid://0ubnfkes0d4r\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_window_relative.gd",
    "content": "## Converts the value of the input into window-relative units between 0 and 1.\n## E.g. if a mouse cursor moves half a screen to the right and down, then \n## this modifier will return (0.5, 0.5).\n@tool\nclass_name GUIDEModifierWindowRelative\nextends GUIDEModifier\n\nfunc is_same_as(other:GUIDEModifier) -> bool:\n\treturn other is GUIDEModifierWindowRelative\n\nfunc _modify_input(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> Vector3:\n\tif not input.is_finite():\n\t\treturn Vector3.INF\n\t\t\n\tvar window = Engine.get_main_loop().get_root()\n\t# We want real pixels, so we need to factor in any scaling that the window does.\n\tvar window_size:Vector2 = window.get_screen_transform().affine_inverse() * Vector2(window.size)\n\treturn Vector3(input.x / window_size.x, input.y / window_size.y, input.z)\n\n\nfunc _editor_name() -> String:\n\treturn \"Window relative\"\n\n\nfunc _editor_description() -> String:\n\treturn \"Converts the value of the input into window-relative units between 0 and 1.\\n\" + \\\n\t\t\t\"E.g. if a mouse cursor moves half a screen to the right and down, then \\n\" + \\\n\t\t\t\"this modifier will return (0.5, 0.5).\"\n"
  },
  {
    "path": "addons/guide/modifiers/guide_modifier_window_relative.gd.uid",
    "content": "uid://cgy4anjdob2tp\n"
  },
  {
    "path": "addons/guide/plugin.cfg",
    "content": "[plugin]\n\nname=\"Godot Unified Input Detection Engine (G.U.I.D.E)\"\ndescription=\"\"\nauthor=\"Jan Thomä\"\nversion=\"0.12.0\"\nscript=\"plugin.gd\"\n"
  },
  {
    "path": "addons/guide/plugin.gd",
    "content": "@tool\nextends EditorPlugin\n\nconst GUIDEProjectSettings = preload(\"editor/guide_project_settings.gd\")\nconst MainPanel:PackedScene = preload(\"editor/mapping_context_editor/mapping_context_editor.tscn\")\n\nvar _main_panel:Control\n\n\nfunc _enable_plugin() -> void:\n\tadd_autoload_singleton(\"GUIDE\", \"res://addons/guide/guide.gd\")\n\t\nfunc _enter_tree() -> void:\n\t_main_panel = MainPanel.instantiate()\n\t_main_panel.initialize(self)\n\tEditorInterface.get_editor_main_screen().add_child(_main_panel)\n\tGUIDEProjectSettings.initialize()\n\t# Hide the main panel. Very much required.\n\t_make_visible(false)\n\nfunc _exit_tree() -> void:\n\tif is_instance_valid(_main_panel):\n\t\t_main_panel.queue_free()\n\tGUIDEInputFormatter.cleanup()\n\nfunc _disable_plugin() -> void:\n\tremove_autoload_singleton(\"GUIDE\")\n\t\n\nfunc _edit(object) -> void:\n\tif object is GUIDEMappingContext:\n\t\t_main_panel.edit(object)\n\nfunc _get_plugin_name() -> String:\n\treturn \"G.U.I.D.E\"\n\nfunc _get_plugin_icon() -> Texture2D:\n\treturn preload(\"res://addons/guide/editor/logo_editor_small.svg\")\n\nfunc _has_main_screen() -> bool:\n\treturn true\n\nfunc _handles(object:Variant) -> bool:\n\treturn object is GUIDEMappingContext\n\nfunc _make_visible(visible) -> void:\n\tif is_instance_valid(_main_panel):\n\t\t_main_panel.visible = visible\n"
  },
  {
    "path": "addons/guide/plugin.gd.uid",
    "content": "uid://wstfxvdc0ol0\n"
  },
  {
    "path": "addons/guide/remapping/guide_input_detector.gd",
    "content": "@tool\n## Helper node for detecting inputs. Detects the next input matching a specification and \n## emits a signal with the detected input. \nclass_name GUIDEInputDetector\nextends Node\n\n## The device type for which the input should be filtered.\nconst DeviceType = GUIDEInput.DeviceType\n\n## Which joy index should be used for detected joy events\nenum JoyIndex {\n\t# Use -1, so the detected input will match any joystick\n\tANY = 0,\n\t# Use the actual index of the detected joystick.\n\tDETECTED = 1\n}\n\nenum DetectionState {\n\t# The detector is currently idle.\n\tIDLE = 0,\n\t# The detector is currently counting down before starting the detection.\n\tCOUNTDOWN = 3,\n\t# The detector waits for all abort inputs to be released before starting the detection.\n\tINPUT_PRE_CLEAR = 4,\n\t# The detector is currently detecting input.\n\tDETECTING = 1,\n\t# The detector has finished detecting but is waiting for input to be released after the detection.\n\tINPUT_POST_CLEAR = 2,\n}\n\n## A countdown between initiating a dection and the actual start of the \n## detection. This is useful because when the user clicks a button to\n## start a detection, we want to make sure that the player is actually\n## ready (and not accidentally moves anything). If set to 0, no countdown\n## will be started.\n@export_range(0, 2, 0.1, \"or_greater\") var detection_countdown_seconds:float = 0.5\n\n## Minimum amplitude to detect any axis. \n@export_range(0, 1, 0.1, \"or_greater\") var minimum_axis_amplitude:float = 0.2\n\n## If any of these inputs is encountered, the detector will \n## treat this as \"abort detection\". \n@export var abort_detection_on:Array[GUIDEInput] = []\n\n## Which joy index should be returned for detected joy events.\n@export var use_joy_index:JoyIndex = JoyIndex.ANY\n\n## Whether trigger buttons on controllers should be detected when \n## then action value type is limited to boolean.\n@export var allow_triggers_for_boolean_actions:bool = true\n\n## Emitted when the detection has started (e.g. countdown has elapsed).\n## Can be used to signal this to the player.\nsignal detection_started()\n\n## Emitted when the input detector detects an input of the given type.\n## If detection was aborted the given input is null.\nsignal input_detected(input:GUIDEInput)\n\n# The timer for the detection countdown.\nvar _timer:Timer\n\n# The current state of the detection.\nvar _status:DetectionState = DetectionState.IDLE\n# Mapping contexts that were active when the detection started. We need to restore these once the detection is\n# finished or aborted.\nvar _saved_mapping_contexts:Array[GUIDEMappingContext] = []\n\n# The last detected input.\nvar _last_detected_input:GUIDEInput = null\n\nfunc _ready():\n\t# don't run the process function if we are not detecting to not waste resources\n\tset_process(false)\n\t_timer = Timer.new()\n\t_timer.one_shot = true\n\tadd_child(_timer, false, Node.INTERNAL_MODE_FRONT)\n\t_timer.timeout.connect(_begin_detection)\n\t\n\n## Whether the input detector is currently detecting input.\nvar is_detecting:bool:\n\tget: return _status != DetectionState.IDLE\n\nvar _value_type:GUIDEAction.GUIDEActionValueType\nvar _device_types:Array[DeviceType] = []\n\n## Detects a boolean input type.\nfunc detect_bool(device_types:Array[DeviceType] = []) -> void:\n\tdetect(GUIDEAction.GUIDEActionValueType.BOOL, device_types)\n\n\n## Detects a 1D axis input type.\nfunc detect_axis_1d(device_types:Array[DeviceType] = []) -> void:\n\tdetect(GUIDEAction.GUIDEActionValueType.AXIS_1D, device_types)\n\n\t\n## Detects a 2D axis input type.\nfunc detect_axis_2d(device_types:Array[DeviceType] = []) -> void:\n\tdetect(GUIDEAction.GUIDEActionValueType.AXIS_2D, device_types)\n\n\n## Detects a 3D axis input type.\nfunc detect_axis_3d(device_types:Array[DeviceType] = []) -> void:\n\tdetect(GUIDEAction.GUIDEActionValueType.AXIS_3D, device_types)\n\n\n## Detects the given input type. If device types are given\n## will only detect inputs from the given device types. \n## Otherwise will detect inputs from all supported device types.\nfunc detect(value_type:GUIDEAction.GUIDEActionValueType,\n\t\tdevice_types:Array[DeviceType] = []) -> void:\n\tif device_types == null:\n\t\tpush_error(\"Device types must not be null. Supply an empty array if you want to detect input from all devices.\")\n\t\treturn\n\t\t\n\tif device_types.has(DeviceType.TOUCH):\n\t\tpush_error(\"Detecting touch events is not supported.\")\n\t\n\n\t# If we are already detecting, abort this.\n\tif _status == DetectionState.DETECTING or _status == DetectionState.INPUT_PRE_CLEAR or _status == DetectionState.INPUT_POST_CLEAR:\n\t\tfor input in abort_detection_on:\n\t\t\tinput._end_usage()\n\t\n\t# and start a new detection.\n\t_status = DetectionState.COUNTDOWN\n\t\n\t_value_type = value_type\n\t_device_types = device_types\n\t_timer.stop()\n\t\n\tif detection_countdown_seconds > 0:\n\t\t_timer.start(detection_countdown_seconds)\n\telse:\n\t\t_begin_detection.call_deferred()\n\t\t\n## This is called by the timer when the countdown has elapsed.\nfunc _begin_detection():\n\t# when we begin we want to make sure that any abort input is\n\t# currently not actuated, otherwise we'd get wrong results.\n\t# therefore we now run a pre-clear phase where we ensure that \n\t# abort input is not pressed.\n\t\n\t_status = DetectionState.INPUT_PRE_CLEAR\n\n\t# enable all abort detection inputs\n\tfor input in abort_detection_on:\n\t\tinput._state = GUIDE._input_state\n\t\tinput._begin_usage()\n\n\t# we also use this inside the editor where the GUIDE\n\t# singleton is not active. Here we don't need to enable\n\t# and disable the mapping contexts.\t\t\n\tif not Engine.is_editor_hint():\t\n\t\t# save currently active mapping contexts\n\t\t_saved_mapping_contexts = GUIDE.get_enabled_mapping_contexts()\n\t\t\n\t\t# disable all mapping contexts\n\t\tfor context in _saved_mapping_contexts:\n\t\t\tGUIDE.disable_mapping_context(context)\n\t\t\t\n\t# enable _process so we can start the pre-clear phase\n\t# once the _process function has determined that no abort input\n\t# is currently pressed, it will switch to detection phase\n\tset_process(true)\n\n## Aborts a running detection. If no detection currently runs\n## does nothing.\nfunc abort_detection() -> void:\n\t_timer.stop()\n\t# if we are currently detecting, deliver the null result\n\t# which will gracefully shut down everything\n\tif _status == DetectionState.DETECTING:\n\t\t_deliver(null)\n\n\t# in any other state we don't need to do anything\n\n## This is called while we are waiting for input to be released.\nfunc _process(delta: float) -> void:\n\t# if we are not waiting for input clear, we don't need to do anything\n\tif _status != DetectionState.INPUT_PRE_CLEAR and _status != DetectionState.INPUT_POST_CLEAR:\n\t\tset_process(false)\n\t\treturn\n\n\t# check if the input is still actuated. We do this to avoid the problem\n\t# of this input accidentally triggering something in the mapping contexts\n\t# when we enable them again.\n\tfor input in abort_detection_on:\n\t\tif input._value.is_finite() and input._value.length() > 0:\n\t\t\t# we still have input, so we are still waiting\n\t\t\t# retry next frame\n\t\t\treturn\n\t\t\t\n\t# if we are here, the input is no longer actuated\n\t\n\tif _status == DetectionState.INPUT_PRE_CLEAR:\n\t\t# pre-clear phase is finished, we can now start the actual\n\t\t# detection\n\t\t# print(\"pre-clear done, detecting now\")\n\t\t_status = DetectionState.DETECTING\n\t\tset_process(false)\n\t\tdetection_started.emit()\n\t\treturn\n\t\n\t# otherwise, this was post-clear, so we can tear down the whole\n\t# thing.\n\t\n\t# tear down the inputs\n\tfor input in abort_detection_on:\n\t\tinput._end_usage()\n\t\n\t# restore the mapping contexts\n\t# but only when not running in the editor\n\tif not Engine.is_editor_hint():\n\t\tfor context in _saved_mapping_contexts:\n\t\t\tGUIDE.enable_mapping_context(context)\n\t\t\n\t# set status to idle\n\t_status = DetectionState.IDLE\n\t# and deliver the detected input\n\tinput_detected.emit(_last_detected_input)\n\n## This is called in any state when input is received.\nfunc _input(event:InputEvent) -> void:\n\tif _status == DetectionState.IDLE:\n\t\treturn\n\t\t\n\t# print(event)\n\n\t# while detecting, we're the only ones consuming input and we eat this input\n\t# to not accidentally trigger built-in Godot mappings (e.g. UI stuff)\n\tget_viewport().set_input_as_handled()\n\t# but we still feed it into GUIDE's global state so this state stays \n\t# up to date. This should have no effect because we disabled all mapping\n\t# contexts.\n\tif not Engine.is_editor_hint():\n\t\tGUIDE.inject_input(event)\t\n\n\tif _status == DetectionState.DETECTING:\n\t\t# check if any abort input will trigger\n\t\tfor input in abort_detection_on:\n\t\t\t# if it triggers, we abort\n\t\t\tif input._value.is_finite() and input._value.length() > 0:\n\t\t\t\t# print(\"abort\")\n\t\t\t\tabort_detection()\n\t\t\t\treturn\t\n\t\t\t\n\t\t# check if the event matches the device type we are\n\t\t# looking for\t\n\t\tif not _matches_device_types(event):\n\t\t\treturn\n\t\t\n\t\t# then check if it can be mapped to the desired \n\t\t# value type\t\n\t\tmatch _value_type:\n\t\t\tGUIDEAction.GUIDEActionValueType.BOOL:\n\t\t\t\t_try_detect_bool(event)\n\t\t\tGUIDEAction.GUIDEActionValueType.AXIS_1D:\n\t\t\t\t_try_detect_axis_1d(event)\n\t\t\tGUIDEAction.GUIDEActionValueType.AXIS_2D:\n\t\t\t\t_try_detect_axis_2d(event)\n\t\t\tGUIDEAction.GUIDEActionValueType.AXIS_3D:\n\t\t\t\t_try_detect_axis_3d(event)\n\n\nfunc _matches_device_types(event:InputEvent) -> bool:\n\tif _device_types.is_empty():\n\t\treturn true\n\t\n\tif event is InputEventKey:\n\t\treturn _device_types.has(DeviceType.KEYBOARD)\n\t\t\n\tif event is InputEventMouse:\n\t\treturn _device_types.has(DeviceType.MOUSE)\n\t\t\n\tif event is InputEventJoypadButton or event is InputEventJoypadMotion:\n\t\treturn _device_types.has(DeviceType.JOY)\t\n\n\treturn false\n\n\t\t\t\nfunc _try_detect_bool(event:InputEvent) -> void:\n\t# we always detect key input on release to avoid\n\t# edge cases with multiple key invocations and modifier keys.\n\t\n\tif event is InputEventKey and event.is_released():\n\t\tvar result := GUIDEInputKey.new()\n\t\tresult.key = event.physical_keycode\n\t\tresult.shift = event.shift_pressed\n\t\tresult.control = event.ctrl_pressed\n\t\tresult.meta = event.meta_pressed\n\t\tresult.alt = event.alt_pressed\n\t\t_deliver(result)\n\t\treturn\n\t\t\n\tif event is InputEventMouseButton and event.is_released():\n\t\tvar result := GUIDEInputMouseButton.new()\n\t\tresult.button = event.button_index\n\t\t_deliver(result)\n\t\treturn\n\t\t\n\tif event is InputEventJoypadButton and event.is_released():\n\t\tvar result := GUIDEInputJoyButton.new()\n\t\tresult.button = event.button_index\n\t\tresult.joy_index = _find_joy_index(event.device)\n\t\t_deliver(result)\n\t\t\n\tif allow_triggers_for_boolean_actions:\n\t\t# only allow joypad trigger buttons\n\t\tif not (event is InputEventJoypadMotion):\n\t\t\treturn\n\t\tif event.axis != JOY_AXIS_TRIGGER_LEFT and \\\n\t\t\t\tevent.axis != JOY_AXIS_TRIGGER_RIGHT:\n\t\t\treturn\n\t\t\t\n\t\tvar result := GUIDEInputJoyAxis1D.new()\n\t\tresult.axis = event.axis\n\t\tresult.joy_index =  _find_joy_index(event.device)\n\t\t_deliver(result)\n\t\t\t\t\t\n\t\t\n\t\t\nfunc _try_detect_axis_1d(event:InputEvent) -> void:\n\t# we sometimes get motion events where no relative movement happened, we are only\n\t# interested in the ones where it does\n\tif event is InputEventMouseMotion and event.relative.length_squared() > 0:\n\t\tvar result := GUIDEInputMouseAxis1D.new()\n\t\t# Pick the direction in which the mouse was moved more.\n\t\tif abs(event.relative.x) > abs(event.relative.y):\n\t\t\tresult.axis\t= GUIDEInputMouseAxis1D.GUIDEInputMouseAxis.X \n\t\telse:\n\t\t\tresult.axis\t= GUIDEInputMouseAxis1D.GUIDEInputMouseAxis.Y\n\t\t_deliver(result)\n\t\treturn\n\n\tif event is InputEventJoypadMotion:\n\t\tif abs(event.axis_value) < minimum_axis_amplitude:\n\t\t\treturn\n\t\t\t\n\t\tvar result := GUIDEInputJoyAxis1D.new()\n\t\tresult.axis = event.axis\n\t\tresult.joy_index = _find_joy_index(event.device)\n\t\t_deliver(result)\n\t\t\n\t\t\t\nfunc _try_detect_axis_2d(event:InputEvent) -> void:\n\tif event is InputEventMouseMotion:\n\t\tvar result := GUIDEInputMouseAxis2D.new()\n\t\t_deliver(result)\n\t\treturn\n\t\t\n\tif event is InputEventJoypadMotion:\n\t\tif event.axis_value < minimum_axis_amplitude:\n\t\t\treturn\n\n\t\tvar result := GUIDEInputJoyAxis2D.new()\n\t\tmatch event.axis:\n\t\t\tJOY_AXIS_LEFT_X, JOY_AXIS_LEFT_Y:\n\t\t\t\tresult.x = JOY_AXIS_LEFT_X\n\t\t\t\tresult.y = JOY_AXIS_LEFT_Y\n\t\t\tJOY_AXIS_RIGHT_X, JOY_AXIS_RIGHT_Y:\n\t\t\t\tresult.x = JOY_AXIS_RIGHT_X\n\t\t\t\tresult.y = JOY_AXIS_RIGHT_Y\n\t\t\t_:\n\t\t\t\t# not supported for detection\n\t\t\t\treturn\n\t\tresult.joy_index = _find_joy_index(event.device)\n\t\t_deliver(result)\n\t\treturn\n\t\t\t\n\nfunc _try_detect_axis_3d(event:InputEvent) -> void:\n\t# currently no input for 3D\n\tpass\t\t\n\n\nfunc _find_joy_index(device_id:int) -> int:\n\tif use_joy_index == JoyIndex.ANY:\n\t\treturn -1\n\t\n\tvar pads := Input.get_connected_joypads()\n\tfor i in pads.size():\n\t\tif pads[i] == device_id:\n\t\t\treturn i\n\t\t\t\n\treturn -1\n\nfunc _deliver(input:GUIDEInput) -> void:\n\t# print(\"deliver\" , input)\n\t_last_detected_input = input\n\t_status = DetectionState.INPUT_POST_CLEAR\n\t# enable processing so we can check if the input is released before we re-enable GUIDE's mapping contexts\n\tset_process(true)\n"
  },
  {
    "path": "addons/guide/remapping/guide_input_detector.gd.uid",
    "content": "uid://db27ccgomq455\n"
  },
  {
    "path": "addons/guide/remapping/guide_remapper.gd",
    "content": "class_name GUIDERemapper\n\n## Emitted when the bound input of an item changes. \nsignal item_changed(item:ConfigItem, input:GUIDEInput)\n\nvar _remapping_config:GUIDERemappingConfig = GUIDERemappingConfig.new()\nvar _mapping_contexts:Array[GUIDEMappingContext] = []\n\nconst GUIDESet = preload(\"../guide_set.gd\")\n\n## Loads the default bindings as they are currently configured in the mapping contexts and a mapping\n## config for editing. Note that the given mapping config will not be modified, so editing can be\n## cancelled. Call get_mapping_config to get the modified mapping config.\nfunc initialize(mapping_contexts:Array[GUIDEMappingContext], remapping_config:GUIDERemappingConfig):\n\t_remapping_config = remapping_config.duplicate() if remapping_config != null else GUIDERemappingConfig.new()\n\t\n\t_mapping_contexts.clear()\n\t\n\tfor mapping_context in mapping_contexts:\n\t\tif not is_instance_valid(mapping_context):\n\t\t\tpush_error(\"Cannot add null mapping context. Ignoring.\")\n\t\t\treturn\n\t\t_mapping_contexts.append(mapping_context)\n\t\t\t\n\t\t\t\n## Returns the mapping config with all modifications applied.\nfunc get_mapping_config() -> GUIDERemappingConfig:\n\treturn _remapping_config.duplicate()\n\t\n\t\nfunc set_custom_data(key:Variant, value:Variant):\n\t_remapping_config.custom_data[key] = value\n\t\n\t\nfunc get_custom_data(key:Variant, default:Variant = null) -> Variant:\n\treturn _remapping_config.custom_data.get(key, default)\t\n\t\n\t\nfunc remove_custom_data(key:Variant) -> void:\n\t_remapping_config.custom_data.erase(key)\n\t\n\t\n## Returns all remappable items. Can be filtered by context, display category or\n## action.\nfunc get_remappable_items(context:GUIDEMappingContext = null, \n\t\tdisplay_category:String = \"\",\n\t\taction:GUIDEAction = null) -> Array[ConfigItem]:\n\t\n\tif action != null and not action.is_remappable:\n\t\tpush_warning(\"Action filter was set but filtered action is not remappable.\")\n\t\treturn []\n\t\t\n\t\n\tvar result:Array[ConfigItem] = []\n\tfor a_context:GUIDEMappingContext in _mapping_contexts:\n\t\tif context != null and context != a_context:\n\t\t\tcontinue\n\t\tfor action_mapping:GUIDEActionMapping in a_context.mappings:\n\t\t\tvar mapped_action:GUIDEAction = action_mapping.action\n\t\t\t# filter non-remappable actions\n\t\t\tif not mapped_action.is_remappable:\n\t\t\t\tcontinue\n\t\t\t\n\t\t\t# if action filter is set, only pick mappings for this action\n\t\t\tif action != null and action != mapped_action:\n\t\t\t\tcontinue\n\t\t\t\n\t\t\t# make config items\n\t\t\tfor index:int in action_mapping.input_mappings.size():\n\t\t\t\tvar input_mapping:GUIDEInputMapping = action_mapping.input_mappings[index]\n\t\t\t\tif input_mapping.override_action_settings and not input_mapping.is_remappable:\n\t\t\t\t\t# skip non-remappable items\n\t\t\t\t\tcontinue\n\t\t\t\t\n\t\t\t\t# Calculate effective display category\n\t\t\t\tvar effective_display_category:String = \\\n\t\t\t\t\t_get_effective_display_category(mapped_action, input_mapping)\n\n\t\t\t\t# if display category filter is set, only pick mappings \n\t\t\t\t# in this category\n\t\t\t\tif display_category.length() > 0 and effective_display_category != display_category:\n\t\t\t\t\tcontinue\t\t\n\t\t\t\t\n\t\t\t\tvar item := ConfigItem.new(a_context, action_mapping.action, index, input_mapping)\n\t\t\t\titem_changed.connect(item._item_changed)\n\t\t\t\tresult.append(item)\n\t\t\n\treturn result\n\t\n\t\nstatic func _get_effective_display_category(action:GUIDEAction, input_mapping:GUIDEInputMapping) -> String:\n\tvar result:String = \"\"\n\tif input_mapping.override_action_settings:\n\t\tresult = input_mapping.display_category\n\t\t\n\tif result.is_empty():\n\t\tresult = action.display_category\n\t\n\treturn result\n\t\n\nstatic func _get_effective_display_name(action:GUIDEAction, input_mapping:GUIDEInputMapping) -> String:\n\tvar result:String = \"\"\n\tif input_mapping.override_action_settings:\n\t\tresult = input_mapping.display_name\n\t\t\n\tif result.is_empty():\n\t\tresult = action.display_name\n\t\n\treturn result\n\t\nstatic func _is_effectively_remappable(action:GUIDEAction, input_mapping:GUIDEInputMapping) -> bool:\n\treturn action.is_remappable and ((not input_mapping.override_action_settings) or input_mapping.is_remappable)\n\t\t\n\nstatic func _get_effective_value_type(action:GUIDEAction, input_mapping:GUIDEInputMapping) -> GUIDEAction.GUIDEActionValueType:\n\tif input_mapping.override_action_settings and input_mapping.input != null:\n\t\treturn input_mapping.input._native_value_type()\n\t\t\n\treturn action.action_value_type\n\t\t\n\n## Returns a list of all collisions in all contexts when this new input would be applied to the config item.\nfunc get_input_collisions(item:ConfigItem, input:GUIDEInput) -> Array[ConfigItem]:\n\tif not _check_item(item):\n\t\treturn []\n\tvar result:Array[ConfigItem] = [] \n\t\n\tif input == null:\n\t\t# no item collides with absent input\n\t\treturn result\n\t\n\t# walk over all known contexts and find any mappings.\n\tfor context:GUIDEMappingContext in _mapping_contexts:\n\t\tfor action_mapping:GUIDEActionMapping in context.mappings:\n\t\t\tfor index:int in action_mapping.input_mappings.size():\n\t\t\t\tvar action := action_mapping.action\n\t\t\t\tif context == item.context and action == item.action and index == item.index:\n\t\t\t\t\t# collisions with self are allowed\n\t\t\t\t\tcontinue\n\n\t\t\t\tvar input_mapping:GUIDEInputMapping = action_mapping.input_mappings[index]\n\t\t\t\tvar bound_input:GUIDEInput = input_mapping.input\n\t\t\t\t# check if this is currently overridden\n\t\t\t\tif _remapping_config._has(context, action, index):\n\t\t\t\t\tbound_input = _remapping_config._get_bound_input_or_null(context, action, index)\n\t\t\t\t\t\n\t\t\t\t# We have a collision\t\n\t\t\t\tif bound_input != null and bound_input.is_same_as(input):\n\t\t\t\t\tvar collision_item := ConfigItem.new(context, action, index, input_mapping)\n\t\t\t\t\titem_changed.connect(collision_item._item_changed)\n\t\t\t\t\tresult.append(collision_item)\n\n\treturn result\n\n\n## Gets the input currently bound to the action in the given context. Can be null if the input\n## is currently not bound.\nfunc get_bound_input_or_null(item:ConfigItem) -> GUIDEInput:\n\tif not _check_item(item):\n\t\treturn null\n\n\t# If the remapping config has a binding for this, this binding wins.\n\tif _remapping_config._has(item.context, item.action, item.index):\n\t\treturn _remapping_config._get_bound_input_or_null(item.context, item.action, item.index)\n\t\t\n\t# otherwise return the default binding for this action in the context\n\tfor action_mapping:GUIDEActionMapping in item.context.mappings:\n\t\tif action_mapping.action == item.action:\n\t\t\tif action_mapping.input_mappings.size() > item.index:\n\t\t\t\treturn action_mapping.input_mappings[item.index].input\n\t\t\telse:\n\t\t\t\tpush_error(\"Action mapping does not have an index of \", item.index , \".\")\n\t\t\t\t\n\treturn null\n\t\n## Sets the bound input to the new value for the given config item. Ignores collisions\n## because collision resolution is highly game specific. Use get_input_collisions to find\n## potential collisions and then resolve them in a way that suits the game. Note that \n## bound input can be set to null, which deliberately unbinds the input. If you want\n## to restore the defaults, call restore_default instead.\nfunc set_bound_input(item:ConfigItem, input:GUIDEInput) -> void:\n\tif not _check_item(item):\n\t\treturn\n\t\t\n\t# first remove any custom binding we have \n\t_remapping_config._clear(item.context, item.action, item.index)\n\t\n\t# Now check if the input is the same as the default\n\tvar bound_input:GUIDEInput = get_bound_input_or_null(item)\n\t\n\tif bound_input == null and input == null:\n\t\titem_changed.emit(item, input)\n\t\treturn # nothing to do\n\t\n\tif bound_input == null:\n\t\t_remapping_config._bind(item.context, item.action, input, item.index)\n\t\titem_changed.emit(item, input)\n\t\treturn\n\t\t\n\tif bound_input != null and input != null and bound_input.is_same_as(input):\n\t\titem_changed.emit(item, input)\n\t\treturn # nothing to do\t\n\t\t\n\t_remapping_config._bind(item.context, item.action, input, item.index)\n\titem_changed.emit(item, input)\n\t\t\n\t\t\n## Returns the default binding for the given config item.\nfunc get_default_input(item:ConfigItem) -> GUIDEInput:\n\tif not _check_item(item):\n\t\treturn null\n\t\t\n\tfor mapping:GUIDEActionMapping in item.context.mappings:\n\t\tif mapping.action == item.action:\n\t\t\t# _check_item verifies the index exists, so no need to check here.\n\t\t\treturn mapping.input_mappings[item.index].input\n\t\t\t\t\n\treturn null\n\t\n\n## Restores the default binding for the given config item. Note that this may\n## introduce a conflict if other bindings have bound conflicting input. You can\n## call get_default_input for the given item to get the default input and then\n## call get_input_collisions for that to find out whether you would get a collision.\nfunc restore_default_for(item:ConfigItem) -> void:\n\tif not _check_item(item):\n\t\treturn\n\n\t_remapping_config._clear(item.context, item.action, item.index)\n\titem_changed.emit(item, get_bound_input_or_null(item))\n\n\n\t\t\n## Verifies that the given item is valid.\nfunc _check_item(item:ConfigItem) -> bool:\n\tif not _mapping_contexts.has(item.context):\n\t\tpush_error(\"Given context is not known to this mapper. Did you call initialize()?\")\n\t\treturn false\n\t\n\tvar action_found := false\n\tvar size_ok := false\n\tfor mapping in item.context.mappings:\n\t\tif mapping.action == item.action:\n\t\t\taction_found = true\n\t\t\tif mapping.input_mappings.size() > item.index and item.index >= 0:\n\t\t\t\tsize_ok = true\n\t\t\tbreak\n\t\t\t\n\tif not action_found:\n\t\tpush_error(\"Given action does not belong to the given context.\")\n\t\treturn false\n\t\t\n\tif not size_ok:\n\t\tpush_error(\"Given index does not exist for the given action's input binding.\")\n\n\n\tif not item.action.is_remappable:\n\t\tpush_error(\"Given action is not remappable.\")\n\t\treturn false\n\t\t\n\treturn true\n\t\n\nclass ConfigItem:\n\t## Emitted when the input to this item has changed.\n\tsignal changed(input:GUIDEInput)\n\t\n\tvar _input_mapping:GUIDEInputMapping\n\t\n\t## The display category for this config item\n\tvar display_category:String:\n\t\tget: return GUIDERemapper._get_effective_display_category(action, _input_mapping)\n\t\n\t## The display name for this config item.\n\tvar display_name:String:\n\t\tget: return GUIDERemapper._get_effective_display_name(action, _input_mapping)\n\t\t\n\t## Whether this item is remappable.\n\tvar is_remappable:bool:\n\t\tget: return GUIDERemapper._is_effectively_remappable(action, _input_mapping)\n\t\t\n\t## The value type for this config item.\n\tvar value_type:GUIDEAction.GUIDEActionValueType:\n\t\tget: return GUIDERemapper._get_effective_value_type(action, _input_mapping)\n\t\n\tvar context:GUIDEMappingContext\t\n\tvar action:GUIDEAction\n\tvar index:int\n\t\n\tfunc _init(context:GUIDEMappingContext, action:GUIDEAction, index:int, input_mapping:GUIDEInputMapping):\n\t\tself.context = context\n\t\tself.action = action\n\t\tself.index = index\n\t\t_input_mapping = input_mapping\n\t\n\t## Checks whether this config item is the same as some other\n\t## e.g. refers to the same input mapping.\t\n\tfunc is_same_as(other:ConfigItem) -> bool:\n\t\treturn context == other.context and \\\n\t\t\t\taction == other.action and \\\n\t\t\t\tindex == other.index\n\t\t\t\n\tfunc _item_changed(item:ConfigItem, input:GUIDEInput):\n\t\tif item.is_same_as(self):\n\t\t\tchanged.emit(input)\n\t\t\n"
  },
  {
    "path": "addons/guide/remapping/guide_remapper.gd.uid",
    "content": "uid://nh78h8tprhwn\n"
  },
  {
    "path": "addons/guide/remapping/guide_remapping_config.gd",
    "content": "@icon(\"res://addons/guide/guide_internal.svg\")\n## A remapping configuration. This only holds changes to the context mapping,\n## so to get the full input map you need to apply this on top of one or more\n## mapping contexts. The settings from this config take precedence over the\n## settings from the mapping contexts.\nclass_name GUIDERemappingConfig\nextends Resource\n\n## Dictionary with remapped inputs. Structure is:\n## { \n##    mapping_context : {\n##         action : {\n##            index : bound input\n##             ...\n##         }, ...\n## }\t\t\n## The bound input can be NULL which means that this was deliberately unbound.\t\n@export var remapped_inputs:Dictionary = {}\n\n## Dictionary for additional custom data to store (e.g. modifier settings, etc.)\n## Note that this data is completely under application control and it's the responsibility\n## of the application to ensure that this data is serializable and gets applied at\n## the necessary point in time.\n@export var custom_data:Dictionary = {}\n\n## Binds the given input to the given action. Index can be given to have \n## alternative bindings for the same action.\nfunc _bind(mapping_context:GUIDEMappingContext, action:GUIDEAction, input:GUIDEInput, index:int = 0) -> void:\n\tif not remapped_inputs.has(mapping_context):\n\t\tremapped_inputs[mapping_context] = {}\n\t\t\n\tif not remapped_inputs[mapping_context].has(action):\n\t\tremapped_inputs[mapping_context][action] = {}\n\t\t\n\tremapped_inputs[mapping_context][action][index] = input\n\t\n\t\n## Unbinds the given input from the given action. This is a deliberate unbind\n## which means that the action should not be triggerable by the input anymore. It \n## its not the same as _clear.\t\nfunc _unbind(mapping_context:GUIDEMappingContext, action:GUIDEAction, index:int = 0) -> void:\n\t_bind(mapping_context, action, null, index)\n\n\t\n## Removes the given input action binding from this configuration. The action will\n## now have the default input that it has in the mapping_context. This is not the \n## same as _unbind.\t\nfunc _clear(mapping_context:GUIDEMappingContext, action:GUIDEAction, index:int = 0) -> void:\n\tif not remapped_inputs.has(mapping_context):\n\t\treturn\n\t\t\n\tif not remapped_inputs[mapping_context].has(action):\n\t\treturn\n\t\t\n\tremapped_inputs[mapping_context][action].erase(index)\n\t\n\tif remapped_inputs[mapping_context][action].is_empty():\n\t\tremapped_inputs[mapping_context].erase(action)\n\t\n\tif remapped_inputs[mapping_context].is_empty():\n\t\tremapped_inputs.erase(mapping_context)\n\t\n\t\n## Returns the bound input for the given action name and index. Returns null\n## if there is matching binding.\nfunc _get_bound_input_or_null(mapping_context:GUIDEMappingContext, action:GUIDEAction, index:int = 0) -> GUIDEInput:\n\tif not remapped_inputs.has(mapping_context):\n\t\treturn null\n\t\t\n\tif not remapped_inputs[mapping_context].has(action):\n\t\treturn null\n\t\t\n\treturn remapped_inputs[mapping_context][action].get(index, null)\n\t\n\t\n## Returns whether or not this mapping has a configuration for the given combination (even if the \n## combination is set to null).\nfunc _has(mapping_context:GUIDEMappingContext, action:GUIDEAction, index:int = 0) -> bool:\n\tif not remapped_inputs.has(mapping_context):\n\t\treturn false\n\t\t\n\tif not remapped_inputs[mapping_context].has(action):\n\t\treturn false\n\t\t\n\treturn remapped_inputs[mapping_context][action].has(index)\n"
  },
  {
    "path": "addons/guide/remapping/guide_remapping_config.gd.uid",
    "content": "uid://bjnghv2v2qu6w\n"
  },
  {
    "path": "addons/guide/triggers/guide_trigger.gd",
    "content": "@tool\n@icon(\"res://addons/guide/triggers/guide_trigger.svg\")\nclass_name GUIDETrigger\nextends Resource\n\nenum GUIDETriggerState {\n\t## The trigger did not fire.\n\tNONE,\n\t## The trigger's conditions are partially met\n\tONGOING,\n\t## The trigger has fired.\n\tTRIGGERED\n}\n\nenum GUIDETriggerType {\n\t# If there are more than one explicit triggers at least one must trigger\n\t# for the action to trigger.\n\tEXPLICIT = 1,\n\t# All implicit triggers must trigger for the action to trigger.\n\tIMPLICIT = 2,\n\t# All blocking triggers prevent the action from triggering.\n\tBLOCKING = 3\n}\n\n\n@export var actuation_threshold:float = 0.5\nvar _last_value:Vector3\n\n## Returns whether this trigger is the same as the other trigger.\n## This is used to determine if a trigger can be reused during context switching.\nfunc is_same_as(other:GUIDETrigger) -> bool:\n\treturn self == other\n\n## Creates a clone of this trigger suitable for context merging.\n##\n## Returns a new trigger instance while preserving references to actions and other\n## shared resources. This ensures triggers watch the same actions across contexts.\n##\n## Default implementation uses shallow copy. Subclasses with sub-resources or arrays\n## should override this method to properly duplicate their internal structures while\n## still preserving action references.\nfunc clone() -> GUIDETrigger:\n\treturn duplicate()\n\n## Returns the trigger type of this trigger.\nfunc _get_trigger_type() -> GUIDETriggerType:\n\treturn GUIDETriggerType.EXPLICIT\n\n\nfunc _update_state(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> GUIDETriggerState:\n\treturn GUIDETriggerState.NONE\n\n\t\nfunc _is_actuated(input:Vector3, value_type:GUIDEAction.GUIDEActionValueType) -> bool:\n\tmatch value_type:\n\t\tGUIDEAction.GUIDEActionValueType.AXIS_1D, GUIDEAction.GUIDEActionValueType.BOOL:\n\t\t\treturn _is_axis1d_actuated(input)\n\t\tGUIDEAction.GUIDEActionValueType.AXIS_2D:\n\t\t\treturn _is_axis2d_actuated(input)\n\t\tGUIDEAction.GUIDEActionValueType.AXIS_3D:\n\t\t\treturn _is_axis3d_actuated(input)\n\t\t\t\n\treturn false\n\n## Checks if a 1D input is actuated.\nfunc _is_axis1d_actuated(input:Vector3) -> bool:\n\treturn is_finite(input.x) and abs(input.x) > actuation_threshold\n\t\n## Checks if a 2D input is actuated.\nfunc _is_axis2d_actuated(input:Vector3) -> bool:\n\treturn is_finite(input.x) and is_finite(input.y) and Vector2(input.x, input.y).length_squared() > actuation_threshold * actuation_threshold\n\n## Checks if a 3D input is actuated.\nfunc _is_axis3d_actuated(input:Vector3) -> bool:\n\treturn input.is_finite() and input.length_squared() > actuation_threshold * actuation_threshold\n\t\n## The name as it should be displayed in the editor.\nfunc _editor_name() -> String:\n\treturn \"GUIDETrigger\"\n\t\n## The description as it should be displayed in the editor.\nfunc _editor_description() -> String:\n\treturn \"\"\n"
  },
  {
    "path": "addons/guide/triggers/guide_trigger.gd.uid",
    "content": "uid://x74mnwgr08a7\n"
  },
  {
    "path": "addons/guide/triggers/guide_trigger.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://ca1eiagyinhl7\"\npath=\"res://.godot/imported/guide_trigger.svg-cd87acbd491929cf49a255f8481b0b63.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/triggers/guide_trigger.svg\"\ndest_files=[\"res://.godot/imported/guide_trigger.svg-cd87acbd491929cf49a255f8481b0b63.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=0.5\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/triggers/guide_trigger_chorded_action.gd",
    "content": "## Fires, when the given action is currently triggering. This trigger is implicit,\n## so it will prevent the action from triggering even if other triggers are successful.\n@tool\nclass_name GUIDETriggerChordedAction\nextends GUIDETrigger\n\n@export var action:GUIDEAction\n\nfunc is_same_as(other:GUIDETrigger) -> bool:\n\tif not other is GUIDETriggerChordedAction:\n\t\treturn false\n\treturn action == other.action\n\nfunc _get_trigger_type() -> GUIDETriggerType: \n\treturn GUIDETriggerType.IMPLICIT\n\nfunc _update_state(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> GUIDETriggerState:\n\tif action == null:\n\t\tpush_warning(\"Chorded trigger without action will never trigger.\")\n\t\treturn GUIDETriggerState.NONE\n\t\n\tif action.is_triggered():\n\t\treturn GUIDETriggerState.TRIGGERED\n\treturn GUIDETriggerState.NONE\n\n\nfunc _editor_name() -> String:\n\treturn \"Chorded Action\"\n\nfunc _editor_description() -> String:\n\treturn \"Fires, when the given action is currently triggering. This trigger is implicit,\\n\" + \\\n \t\t\t\t\"so it will prevent the action from triggering even if other triggers are successful.\"\n"
  },
  {
    "path": "addons/guide/triggers/guide_trigger_chorded_action.gd.uid",
    "content": "uid://brsxcrai2te83\n"
  },
  {
    "path": "addons/guide/triggers/guide_trigger_combo.gd",
    "content": "@tool\nclass_name GUIDETriggerCombo\nextends GUIDETrigger\n\nenum ActionEventType {\n\tTRIGGERED = 1,\n\tSTARTED = 2,\n\tONGOING = 4,\n\tCANCELLED = 8,\n\tCOMPLETED = 16\n}\n\n## If set to true, the combo trigger will print information\n## about state changes to the debug log.\n@export var enable_debug_print:bool = false\n@export var steps:Array[GUIDETriggerComboStep] = []\n@export var cancellation_actions:Array[GUIDETriggerComboCancelAction] = []\n\nvar _current_step:int = -1\nvar _remaining_time:float = 0\n\nfunc is_same_as(other:GUIDETrigger) -> bool:\n\tif not other is GUIDETriggerCombo:\n\t\treturn false\n\tif steps.size() != other.steps.size():\n\t\treturn false\n\tif cancellation_actions.size() != other.cancellation_actions.size():\n\t\treturn false\n\t\t\n\tfor i in range(steps.size()):\n\t\tif not steps[i].is_same_as(other.steps[i]):\n\t\t\treturn false\n\t\t\t\n\tfor i in range(cancellation_actions.size()):\n\t\tif not cancellation_actions[i].is_same_as(other.cancellation_actions[i]):\n\t\t\treturn false\n\n\treturn true\n\n## Creates a clone with properly duplicated step and cancellation action arrays.\n## Each array element is shallow-copied to preserve action references.\nfunc clone() -> GUIDETrigger:\n\tvar copy:GUIDETriggerCombo = duplicate()\n\n\t# Duplicate array structures while preserving action references\n\tcopy.steps = []\n\tfor step:GUIDETriggerComboStep in steps:\n\t\tcopy.steps.append(step.duplicate())\n\n\tcopy.cancellation_actions = []\n\tfor action:GUIDETriggerComboCancelAction in cancellation_actions:\n\t\tcopy.cancellation_actions.append(action.duplicate())\n\n\treturn copy\n\nfunc _update_state(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> GUIDETriggerState:\n\tif steps.is_empty():\n\t\tpush_warning(\"Combo with no steps will never fire.\")\n\t\treturn GUIDETriggerState.NONE\n\n\t# initial setup\n\tif _current_step == -1:\n\t\tfor step in steps:\n\t\t\tstep._prepare()\n\t\tfor action in cancellation_actions:\n\t\t\taction._prepare()\n\t\t_reset()\n\t\t\n\t\t\n\tvar current_action := steps[_current_step].action\n\tif current_action == null:\n\t\tpush_warning(\"Step \", _current_step , \" has no action \", resource_path)\n\t\treturn GUIDETriggerState.NONE\n\t\t\n\t# check if any of our cancellation actions fired\n\tfor action in cancellation_actions:\n\t\t# if the action is the current action we don't count its firing as cancellation\n\t\tif action.action == current_action:\n\t\t\tcontinue\n\t\t\t\n\t\tif action._has_fired:\n\t\t\tif enable_debug_print:\n\t\t\t\tprint(\"Combo cancelled by action '\", action.action._editor_name(), \"'.\")\n\t\t\t_reset()\n\t\t\treturn GUIDETriggerState.NONE\n\t\n\t# check if any of the steps has fired out of order\n\tfor step in steps:\n\t\tif step.action == current_action:\n\t\t\tcontinue\n\t\t\t\n\t\tif step._has_fired:\n\t\t\tif enable_debug_print:\n\t\t\t\tprint(\"Combo out of order step by action '\", step.action._editor_name(), \"'.\")\n\t\t\t_reset()\n\t\t\treturn GUIDETriggerState.NONE\n\t\t\t\n\t# check if we took too long (unless we're in the first step)\n\tif _current_step > 0:\n\t\t_remaining_time -= delta\n\t\tif _remaining_time <= 0.0:\n\t\t\tif enable_debug_print:\n\t\t\t\tprint(\"Step time for step \", _current_step , \" exceeded.\")\n\t\t\t_reset()\n\t\t\treturn GUIDETriggerState.NONE\n\n\t# if the current action was fired, if so advance to the next\n\tif steps[_current_step]._has_fired:\n\t\t# reset this step, so it will not count as misfired next round\n\t\tsteps[_current_step]._has_fired = false\n\t\tif _current_step + 1 >= steps.size():\n\t\t\t# we finished the combo\n\t\t\tif enable_debug_print:\n\t\t\t\tprint(\"Combo fired.\")\n\t\t\t_reset()\n\t\t\treturn GUIDETriggerState.TRIGGERED\n\t\t\t\n\t\t# otherwise, pick the next step\n\t\t_current_step += 1\n\t\tif enable_debug_print:\n\t\t\tprint(\"Combo advanced to step \" , _current_step, \".\")\n\t\t_remaining_time = steps[_current_step].time_to_actuate\n\t\t\n\t\t# Reset all steps and cancellation actions to \"not fired\" in \n\t\t# case they were triggered by this action. Otherwise a double-tap \n\t\t# would immediately fire for both taps once the first is through\n\t\tfor step in steps:\n\t\t\tstep._has_fired = false\n\t\tfor action in cancellation_actions:\n\t\t\taction._has_fired = false\n\t\n\t# and in any case we're still processing.\n\treturn GUIDETriggerState.ONGOING\t\t\n\t\n\t\t\nfunc _reset():\n\tif enable_debug_print:\n\t\tprint(\"Combo reset.\")\n\t_current_step = 0\n\t_remaining_time = steps[0].time_to_actuate\n\tfor step in steps:\n\t\tstep._has_fired = false\n\tfor action in cancellation_actions:\n\t\taction._has_fired = false\t\n\nfunc _editor_name() -> String:\n\treturn \"Combo\"\n\nfunc _editor_description() -> String:\n\treturn \"Fires, when the input exceeds the actuation threshold.\"\n\n"
  },
  {
    "path": "addons/guide/triggers/guide_trigger_combo.gd.uid",
    "content": "uid://biioody2ca0e7\n"
  },
  {
    "path": "addons/guide/triggers/guide_trigger_combo_cancel_action.gd",
    "content": "@icon(\"res://addons/guide/guide_internal.svg\")\nclass_name GUIDETriggerComboCancelAction\nextends Resource\n\n@export var action:GUIDEAction\n@export_flags(\"Triggered:1\", \"Started:2\", \"Ongoing:4\", \"Cancelled:8\",\"Completed:16\") \nvar completion_events:int = GUIDETriggerCombo.ActionEventType.TRIGGERED\n\nvar _has_fired:bool = false\n\nfunc is_same_as(other:GUIDETriggerComboCancelAction) -> bool:\n\treturn action == other.action and \\\n\t\tcompletion_events == other.completion_events\n\nfunc _prepare():\n\tif completion_events & GUIDETriggerCombo.ActionEventType.TRIGGERED:\n\t\taction.triggered.connect(_fired)\n\tif completion_events & GUIDETriggerCombo.ActionEventType.STARTED:\n\t\taction.started.connect(_fired)\n\tif completion_events & GUIDETriggerCombo.ActionEventType.ONGOING:\n\t\taction.ongoing.connect(_fired)\n\tif completion_events & GUIDETriggerCombo.ActionEventType.CANCELLED:\n\t\taction.cancelled.connect(_fired)\n\tif completion_events & GUIDETriggerCombo.ActionEventType.COMPLETED:\n\t\taction.completed.connect(_fired)\n\t_has_fired = false\n\t\t\n\t\t\nfunc _fired():\n\t_has_fired = true\n\t\n"
  },
  {
    "path": "addons/guide/triggers/guide_trigger_combo_cancel_action.gd.uid",
    "content": "uid://ddgp5tashyo8o\n"
  },
  {
    "path": "addons/guide/triggers/guide_trigger_combo_step.gd",
    "content": "@icon(\"res://addons/guide/guide_internal.svg\")\nclass_name GUIDETriggerComboStep\nextends Resource\n\n@export var action:GUIDEAction\n@export_flags(\"Triggered:1\", \"Started:2\", \"Ongoing:4\", \"Cancelled:8\",\"Completed:16\") \nvar completion_events:int = GUIDETriggerCombo.ActionEventType.TRIGGERED\n@export var time_to_actuate:float = 0.5\n\n\nfunc is_same_as(other:GUIDETriggerComboStep) -> bool:\n\treturn action == other.action and \\\n\t\tcompletion_events == other.completion_events and \\\n\t\tis_equal_approx(time_to_actuate, other.time_to_actuate)\n\nvar _has_fired:bool = false\n\nfunc _prepare():\n\tif completion_events & GUIDETriggerCombo.ActionEventType.TRIGGERED:\n\t\taction.triggered.connect(_fired)\n\tif completion_events & GUIDETriggerCombo.ActionEventType.STARTED:\n\t\taction.started.connect(_fired)\n\tif completion_events & GUIDETriggerCombo.ActionEventType.ONGOING:\n\t\taction.ongoing.connect(_fired)\n\tif completion_events & GUIDETriggerCombo.ActionEventType.CANCELLED:\n\t\taction.cancelled.connect(_fired)\n\tif completion_events & GUIDETriggerCombo.ActionEventType.COMPLETED:\n\t\taction.completed.connect(_fired)\n\t_has_fired = false\n\t\t\n\t\t\nfunc _fired():\n\t_has_fired = true\n\t\n"
  },
  {
    "path": "addons/guide/triggers/guide_trigger_combo_step.gd.uid",
    "content": "uid://be47edmg8hwpo\n"
  },
  {
    "path": "addons/guide/triggers/guide_trigger_down.gd",
    "content": "## Fires, when the input exceeds the actuation threshold. This is\n## the default trigger when no trigger is specified.\n@tool\nclass_name GUIDETriggerDown\nextends GUIDETrigger\n\nfunc is_same_as(other:GUIDETrigger) -> bool:\n\treturn other is GUIDETriggerDown\n\nfunc _update_state(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> GUIDETriggerState:\n\t# if the input is actuated, then the trigger is triggered.\n\tif _is_actuated(input, value_type):\n\t\treturn GUIDETriggerState.TRIGGERED\n\t# otherwise, the trigger is not triggered.\n\treturn GUIDETriggerState.NONE\n\n\nfunc _editor_name() -> String:\n\treturn \"Down\"\n\nfunc _editor_description() -> String:\n\treturn \"Fires, when the input exceeds the actuation threshold. This is\\n\" +\\\n\t\t\t\"the default trigger when no trigger is specified.\"\n"
  },
  {
    "path": "addons/guide/triggers/guide_trigger_down.gd.uid",
    "content": "uid://b4cdrn4paoj3i\n"
  },
  {
    "path": "addons/guide/triggers/guide_trigger_hair.gd",
    "content": "@tool\n# Triggers using relative actuation threshold instead of absolute threshold.\n# Triggers when the input presses more than the threshold.\n# Stops when the input unpresses more than the threshold.\nclass_name GUIDETriggerHair\nextends GUIDETrigger\n\nvar _edge_value: float = 0.0\nvar _is_triggered: bool = false\n\nfunc is_same_as(other: GUIDETrigger) -> bool:\n\treturn other is GUIDETriggerHair and \\\n\tis_equal_approx(actuation_threshold, other.actuation_threshold)\n\n\nfunc _update_state(input: Vector3, delta: float, value_type: GUIDEAction.GUIDEActionValueType) -> GUIDETriggerState:\n\t# Hair trigger: symmetric behavior prevents jitter on release/re-trigger.\n\t# Triggered: _edge_value tracks peak, release when dropping BY threshold from peak.\n\t# Not triggered: _edge_value tracks valley, trigger when rising BY threshold from valley.\n\n\tif _is_triggered:\n\t\t# Track peak - update if input increased\n\t\tif input.x > _edge_value:\n\t\t\t_edge_value = input.x\n\n\t\t# Release when dropping BY threshold from peak (with epsilon for float precision)\n\t\tvar drop := _edge_value - input.x\n\t\tif drop >= actuation_threshold - 0.001:\n\t\t\t_is_triggered = false\n\t\t\t_edge_value = input.x  # Switch to tracking valley\n\t\t\treturn GUIDETriggerState.NONE\n\n\t\treturn GUIDETriggerState.TRIGGERED\n\telse:\n\t\t# Track valley - update if input decreased\n\t\tif input.x < _edge_value:\n\t\t\t_edge_value = input.x\n\n\t\t# Trigger when rising BY threshold from valley (with epsilon for float precision)\n\t\tvar rise := input.x - _edge_value\n\t\tif rise >= actuation_threshold - 0.001:\n\t\t\t_is_triggered = true\n\t\t\t_edge_value = input.x  # Switch to tracking peak\n\t\t\treturn GUIDETriggerState.TRIGGERED\n\n\t\treturn GUIDETriggerState.NONE\n\n\nfunc _editor_name() -> String:\n\treturn \"Hair\"\n\nfunc _editor_description() -> String:\n\treturn \"Triggers and resets when a relative threshold is hit.\"\n"
  },
  {
    "path": "addons/guide/triggers/guide_trigger_hair.gd.uid",
    "content": "uid://diaybithpqj5o\n"
  },
  {
    "path": "addons/guide/triggers/guide_trigger_hold.gd",
    "content": "@tool\n## A trigger that activates when the input is held down for a certain amount of time.\nclass_name GUIDETriggerHold\nextends GUIDETrigger\n\n## The time for how long the input must be held.\n@export var hold_treshold:float = 1.0\n## If true, the trigger will only fire once until the input is released. Otherwise the trigger will fire every frame.\n@export var is_one_shot:bool = false\n\nvar _accumulated_time:float = 0\nvar _did_shoot:bool = false\n\nfunc is_same_as(other:GUIDETrigger) -> bool:\n\treturn other is GUIDETriggerHold and \\\n\t\t\tis_one_shot == other.is_one_shot and \\\n\t\t\tis_equal_approx(hold_treshold, other.hold_treshold)\n\nfunc _update_state(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> GUIDETriggerState:\n\t# if the input is actuated, accumulate time and check if the hold threshold has been reached\n\tif _is_actuated(input, value_type):\n\t\t_accumulated_time += delta\n\t\t\n\t\tif _accumulated_time >= hold_treshold:\n\t\t\t# if the trigger is one shot and we already shot, then we will not trigger again.\n\t\t\tif is_one_shot and _did_shoot:\n\t\t\t\treturn GUIDETriggerState.NONE\n\t\t\telse:\n\t\t\t\t# otherwise, we will just trigger.\n\t\t\t\t_did_shoot = true\n\t\t\t\treturn GUIDETriggerState.TRIGGERED\n\t\telse:\n\t\t\t# if the hold threshold has not been reached, then the trigger is ongoing.\n\t\t\treturn GUIDETriggerState.ONGOING\n\telse:\n\t\t# if the input is not actuated, then the trigger is not triggered and we reset the accumulated time.\n\t\t# and our one shot flag.\n\t\t_accumulated_time = 0\n\t\t_did_shoot = false\n\t\treturn GUIDETriggerState.NONE\n\n\nfunc _editor_name() -> String:\n\treturn \"Hold\"\n\nfunc _editor_description() -> String:\n\treturn \"Fires, once the input has remained actuated for hold_threshold seconds.\\n\" + \\\n\t\t\t\"My fire once or repeatedly.\"\n"
  },
  {
    "path": "addons/guide/triggers/guide_trigger_hold.gd.uid",
    "content": "uid://cfvgpvihp74si\n"
  },
  {
    "path": "addons/guide/triggers/guide_trigger_pressed.gd",
    "content": "@tool\n## A trigger that activates when the input is pushed down. Will only emit a\n## trigger event once. Holding the input will not trigger further events.\nclass_name GUIDETriggerPressed\nextends GUIDETrigger\n\n\nfunc is_same_as(other:GUIDETrigger) -> bool:\n\treturn other is GUIDETriggerPressed\n\nfunc _update_state(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> GUIDETriggerState:\n\tif _is_actuated(input, value_type):\n\t\tif not _is_actuated(_last_value, value_type):\n\t\t\treturn GUIDETriggerState.TRIGGERED\n\t\t\n\treturn GUIDETriggerState.NONE\n\n\nfunc _editor_name() -> String:\n\treturn \"Pressed\"\n\n\nfunc _editor_description() -> String:\n\treturn \"Fires once, when the input exceeds actuation threshold. Holding the input\\n\" + \\\n\t\t\"will not fire additional triggers.\"\n"
  },
  {
    "path": "addons/guide/triggers/guide_trigger_pressed.gd.uid",
    "content": "uid://b52rqq28tuqpg\n"
  },
  {
    "path": "addons/guide/triggers/guide_trigger_pulse.gd",
    "content": "@tool\n## A trigger that activates when the input is pushed down and then repeatedly sends trigger events at a fixed interval.\n## Note: the trigger will be either triggering or ongoing until the input is released.\n## Note: at most one pulse will be emitted per frame.\nclass_name GUIDETriggerPulse\nextends GUIDETrigger\n\n## If true, the trigger will trigger immediately when the input is actuated. Otherwise, the trigger will wait for the initial delay.\n@export var trigger_on_start:bool = true\n## The delay after the initial actuation before pulsing begins.\n@export var initial_delay:float = 0.3:\n\tset(value):\n\t\tinitial_delay = max(0, value)\n\n## The interval between pulses. Set to 0 to pulse every frame.\n@export var pulse_interval:float = 0.1:\n\tset(value):\n\t\tpulse_interval = max(0, value)\n\n## Maximum number of pulses. If <= 0, the trigger will pulse indefinitely.\n@export var max_pulses:int = 0\n\nvar _delay_until_next_pulse:float = 0\nvar _emitted_pulses:int = 0\n\nfunc is_same_as(other:GUIDETrigger) -> bool:\n\tif not other is GUIDETriggerPulse:\n\t\treturn false\n\treturn is_equal_approx(initial_delay, other.initial_delay) and \\\n\t\tis_equal_approx(pulse_interval, other.pulse_interval) and \\\n\t\tmax_pulses == other.max_pulses and \\\n\t\ttrigger_on_start == other.trigger_on_start\n\nfunc _update_state(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> GUIDETriggerState:\n\tif _is_actuated(input, value_type):\n\t\tif not _is_actuated(_last_value, value_type):\n\t\t\t# we went from \"not actuated\" to actuated, pulsing starts\n\t\t\t_delay_until_next_pulse = initial_delay\n\t\t\tif trigger_on_start:\n\t\t\t\treturn GUIDETriggerState.TRIGGERED\n\t\t\telse:\n\t\t\t\treturn GUIDETriggerState.ONGOING\n\t\t\t\n\t\t# if we already are pulsing and have exceeded the maximum number of pulses, we will not pulse anymore.\n\t\tif max_pulses > 0 and _emitted_pulses >= max_pulses:\n\t\t\treturn GUIDETriggerState.NONE\t\n\t\t\n\t\t# subtract the delta from the delay until the next pulse\n\t\t_delay_until_next_pulse -= delta\n\t\t\n\t\tif _delay_until_next_pulse > 0:\n\t\t\t# we are still waiting for the next pulse, nothing to do.\n\t\t\treturn GUIDETriggerState.ONGOING\n\t\t\n\t\t# now delta could be larger than our pulse, in which case we loose a few pulses.\n\t\t# as we can pulse at most once per frame.\n\n\t\t# in case someone sets the pulse interval to 0, we will pulse every frame.\n\t\tif is_equal_approx(pulse_interval, 0):\n\t\t\t_delay_until_next_pulse = 0\n\t\t\tif max_pulses > 0:\n\t\t\t\t_emitted_pulses += 1\n\t\t\treturn GUIDETriggerState.TRIGGERED\n\t\t\t\n\t\t# Now add the delay until the next pulse\n\t\t_delay_until_next_pulse += pulse_interval\n\t\t\n\t\t# If the interval is really small, we can potentially have skipped some pulses\n\t\tif _delay_until_next_pulse <= 0: \n\t\t\t# we have skipped some pulses\n\t\t\tvar skipped_pulses:int = int(-_delay_until_next_pulse / pulse_interval)\n\t\t\t_delay_until_next_pulse += skipped_pulses * pulse_interval\n\t\t\tif max_pulses > 0:\n\t\t\t\t_emitted_pulses += skipped_pulses\n\t\t\t\tif _emitted_pulses >= max_pulses:\n\t\t\t\t\treturn GUIDETriggerState.NONE\n\t\t\n\t\t# Record a pulse and return triggered\n\t\tif max_pulses > 0:\n\t\t\t_emitted_pulses += 1\n\t\treturn GUIDETriggerState.TRIGGERED\n\t\t\n\t# if the input is not actuated, then the trigger is not triggered.\n\t_emitted_pulses = 0\n\t_delay_until_next_pulse = 0\n\treturn GUIDETriggerState.NONE\n\t\t\n\t\t\nfunc _editor_name() -> String:\n\treturn \"Pulse\"\t\t\n\n\nfunc _editor_description() -> String:\n\treturn \"Fires at an interval while the input is actuated.\"\n"
  },
  {
    "path": "addons/guide/triggers/guide_trigger_pulse.gd.uid",
    "content": "uid://dna8tkcuk528v\n"
  },
  {
    "path": "addons/guide/triggers/guide_trigger_released.gd",
    "content": "@tool\n## A trigger that activates when the input is released down. Will only emit a\n## trigger event once.\nclass_name GUIDETriggerReleased\nextends GUIDETrigger\n\nfunc is_same_as(other: GUIDETrigger) -> bool:\n\treturn other is GUIDETriggerReleased\n\n\nfunc _update_state(input: Vector3, delta: float, value_type: GUIDEAction.GUIDEActionValueType) -> GUIDETriggerState:\n\t# https://github.com/godotneers/G.U.I.D.E/issues/144 - go to ongoing while input is \n\t# actuated, so code can track position while input is held down. This is useful for \n\t# touch input, where we have no more position information when input is released.\n\tif _is_actuated(input, value_type):\n\t\treturn GUIDETriggerState.ONGOING\n\t\n\tif not _is_actuated(input, value_type):\n\t\tif _is_actuated(_last_value, value_type):\n\t\t\treturn GUIDETriggerState.TRIGGERED\n\t\n\treturn GUIDETriggerState.NONE\n\n\nfunc _editor_name() -> String:\n\treturn \"Released\"\n\n\nfunc _editor_description() -> String:\n\treturn \"Fires once, when the input goes from actuated to not actuated. The opposite of the Pressed trigger.\"\n"
  },
  {
    "path": "addons/guide/triggers/guide_trigger_released.gd.uid",
    "content": "uid://biiggjw6tv4uq\n"
  },
  {
    "path": "addons/guide/triggers/guide_trigger_stability.gd",
    "content": "@tool\n## Triggers depending on whether the input changes while actuated. This trigger is\n## is implicit, so it must succeed for all other triggers to succeed.\nclass_name GUIDETriggerStability\nextends GUIDETrigger\n\nenum TriggerWhen {\n## Input must be stable\n\tINPUT_IS_STABLE,\n## Input must change\n\tINPUT_CHANGES\n}\n## The maximum amount that the input can change after actuation before it is\n## considered \"changed\".\n@export var max_deviation: float = 1\n## When should the trigger trigger?\n@export var trigger_when: TriggerWhen = TriggerWhen.INPUT_IS_STABLE\n\nvar _initial_value: Vector3\nvar _deviated: bool = false\n\n\nfunc is_same_as(other: GUIDETrigger) -> bool:\n\treturn other is GUIDETriggerStability and \\\n\ttrigger_when == other.trigger_when and \\\n\tis_equal_approx(max_deviation, other.max_deviation)\n\n\nfunc _get_trigger_type() -> GUIDETriggerType:\n\treturn GUIDETriggerType.IMPLICIT\n\n\nfunc _update_state(input: Vector3, delta: float, value_type: GUIDEAction.GUIDEActionValueType) -> GUIDETriggerState:\n\tif _is_actuated(input, value_type):\n\t\tif _deviated:\n\t\t\tif trigger_when == TriggerWhen.INPUT_IS_STABLE:\n\t\t\t\treturn GUIDETriggerState.NONE\n\t\t\treturn GUIDETriggerState.TRIGGERED\n\t\t\n\t\tif not _is_actuated(_last_value, value_type):\n\t\t\t# we went from \"not actuated\" to actuated, start\n\t\t\t_initial_value = input\n\t\t\tif trigger_when == TriggerWhen.INPUT_IS_STABLE:\n\t\t\t\treturn GUIDETriggerState.TRIGGERED\n\t\t\telse:\n\t\t\t\treturn GUIDETriggerState.ONGOING\n\t\t\n\t\t# calculate how far the input is from the initial value\n\t\tif _initial_value.distance_squared_to(input) > (max_deviation * max_deviation):\n\t\t\t_deviated = true\n\t\t\tif trigger_when == TriggerWhen.INPUT_IS_STABLE:\n\t\t\t\treturn GUIDETriggerState.NONE\n\t\t\treturn GUIDETriggerState.TRIGGERED\n\t\t\n\t\tif trigger_when == TriggerWhen.INPUT_IS_STABLE:\n\t\t\treturn GUIDETriggerState.TRIGGERED\n\t\t\n\t\treturn GUIDETriggerState.ONGOING\n\t\n\t# if the input is not actuated\n\t_deviated = false\n\treturn GUIDETriggerState.NONE\n\n\nfunc _editor_name() -> String:\n\treturn \"Stability\"\n\n\nfunc _editor_description() -> String:\n\treturn \"Triggers depending on whether the input changes while actuated. This trigger\\n\" +\\\n\t\"is implicit, so it must succeed for all other triggers to succeed.\"\n"
  },
  {
    "path": "addons/guide/triggers/guide_trigger_stability.gd.uid",
    "content": "uid://deoksgw6vfo5v\n"
  },
  {
    "path": "addons/guide/triggers/guide_trigger_tap.gd",
    "content": "@tool\n## A trigger that activates when the input is tapped and released before the time threshold is reached.\nclass_name GUIDETriggerTap\nextends GUIDETrigger\n\n## The time threshold for the tap to be considered a tap.\n@export var tap_threshold: float = 0.2\n\nvar _accumulated_time: float = 0\n\nfunc is_same_as(other: GUIDETrigger) -> bool:\n\treturn other is GUIDETriggerTap and \\\n\t\t\tis_equal_approx(tap_threshold, other.tap_threshold)\n\nfunc _update_state(input: Vector3, delta: float, value_type:GUIDEAction.GUIDEActionValueType) -> GUIDETriggerState:\n\tif _is_actuated(input, value_type):\n\t\t# if the input was actuated before, and the tap threshold has been exceeded, the trigger is locked down\n\t\t# until the input is released and we can exit out early\n\t\tif _is_actuated(_last_value, value_type) and _accumulated_time > tap_threshold:\n\t\t\treturn GUIDETriggerState.NONE\n\n\t\t# accumulate time\n\t\t_accumulated_time += delta\n\n\t\tif _accumulated_time < tap_threshold:\n\t\t\treturn GUIDETriggerState.ONGOING\n\t\telse:\n\t\t\t# we have exceeded the tap threshold, so the tap is not triggered.\n\t\t\treturn GUIDETriggerState.NONE\n\n\telse: # not actuated right now\n\t\t# if the input was actuated before...\n\t\tif _is_actuated(_last_value, value_type):\n\t\t\t# ... and the accumulated time is less than the threshold, then the tap is triggered.\n\t\t\tif _accumulated_time < tap_threshold:\n\t\t\t\t_accumulated_time = 0\n\t\t\t\treturn GUIDETriggerState.TRIGGERED\n\n\t\t\t# Otherwise, the tap is not triggered, but we reset the accumulated time\n\t\t\t# so the trigger is now again ready to be triggered.\n\t\t\t_accumulated_time = 0\n\t\t\t\n \t\t# in either case, the trigger is not triggered.\n\t\treturn GUIDETriggerState.NONE\n\nfunc _editor_name() -> String:\n\treturn \"Tap\"\n\n\nfunc _editor_description() -> String:\n\treturn \"Fires when the input is actuated and released within the given timeframe.\"\n"
  },
  {
    "path": "addons/guide/triggers/guide_trigger_tap.gd.uid",
    "content": "uid://c76fmncyucwqc\n"
  },
  {
    "path": "addons/guide/ui/guide_formatting_utils.gd",
    "content": "@tool\n## Helper functions for icon renderers and text providers\n\n## Supported controller types.\nenum ControllerType {\n\tUNKNOWN = 0,\n\tMICROSOFT = 1,\n\tNINTENDO = 2,\n\tSONY = 3,\n\tSTEAM_DECK = 4,\n}\n\n## Detection strings for the controller types. The key is the controller type, the value is an \n## array of detection strings. All detection strings are case-insensitive. \nstatic var _controller_detection_strings:Dictionary = {\n\tControllerType.MICROSOFT: [\"XBox\", \"XInput\"],\n\tControllerType.NINTENDO: [\"Nintendo Switch\"],\n\tControllerType.SONY : [\"DualSense\", \"DualShock\", \"PlayStation\", \"PS3\", \"PS4\", \"PS5\"],\n\tControllerType.STEAM_DECK: [\"Steam Deck\"]\n} \n\n\n## Determines the effective controller type from the input and formatting options.\nstatic func effective_controller_type(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> ControllerType:\n\t# only joy input can be a controller input\n\tif not (input is GUIDEInputJoyBase):\n\t\treturn ControllerType.UNKNOWN\n\n\t# If the options force a specific joy type, we use that. \n\tif options.joy_rendering == GUIDEInputFormattingOptions.JoyRendering.FORCE_JOY_TYPE:\n\t\treturn _controller_type_from_joy_type(options.preferred_joy_type)\n\n\t# If the options use a fallback, then we try to detect and only use the fallback \n\t# if we couldn't detect the controller type. \t\t\t\n\tvar controller_type = _detect_controller_type(input)\n\n\tif options.joy_rendering == GUIDEInputFormattingOptions.JoyRendering.PREFER_JOY_TYPE:\n\t\tif controller_type == ControllerType.UNKNOWN:\n\t\t\treturn _controller_type_from_joy_type(options.preferred_joy_type)\n\n\t# otherwise return what we have detected\n\treturn controller_type\n\n## Detects the actual controller type of the controller related to the given input. \n## Uses the controller detection strings.\nstatic func _detect_controller_type(input:GUIDEInput) -> ControllerType:\n\tvar haystack = _joy_name_for_input(input).to_lower()\n\t\n\tif haystack == \"\":\n\t\treturn ControllerType.UNKNOWN\n\t\t\t\n\tfor type:ControllerType in _controller_detection_strings.keys():\n\t\tvar strings:Array = _controller_detection_strings[type]\n\t\tfor needle:String in strings:\n\t\t\tif haystack.contains(needle.to_lower()):\n\t\t\t\treturn type\n\t\t\t\t\n\treturn ControllerType.UNKNOWN\n\n\n## Returns the matching controller type from the given joy type. \nstatic func _controller_type_from_joy_type(joy_type:GUIDEInputFormattingOptions.JoyType) -> ControllerType:\n\tmatch joy_type:\n\t\tGUIDEInputFormattingOptions.JoyType.MICROSOFT_CONTROLLER:\n\t\t\treturn ControllerType.MICROSOFT\n\t\tGUIDEInputFormattingOptions.JoyType.NINTENDO_CONTROLLER:\n\t\t\treturn ControllerType.NINTENDO\n\t\tGUIDEInputFormattingOptions.JoyType.SONY_CONTROLLER:\n\t\t\treturn ControllerType.SONY\n\t\tGUIDEInputFormattingOptions.JoyType.STEAM_DECK_CONTROLLER:\n\t\t\treturn ControllerType.STEAM_DECK\n\t\t\n\t\t\t\n\treturn ControllerType.UNKNOWN\n\n\n## Returns the name of the associated joystick/pad of the given input.\n## If the input is no joy input or the device name cannot be determined\n## returns an empty string. \nstatic func _joy_name_for_input(input:GUIDEInput) -> String:\n\tif not input is GUIDEInputJoyBase:\n\t\treturn \"\"\n\t\n\tvar joypads:Array[int] = Input.get_connected_joypads()\n\tvar joy_index:int = input.joy_index\n\tif joy_index < 0:\n\t\t# pick the first one\n\t\tjoy_index = 0\n\t\n\t# We don't have such a controller, so bail out.\n\tif joypads.size() <= joy_index:\n\t\treturn \"\" \n\t\t\n\tvar id := joypads[joy_index]\n\treturn Input.get_joy_name(id)\t\n"
  },
  {
    "path": "addons/guide/ui/guide_formatting_utils.gd.uid",
    "content": "uid://eygt0p2k06vvv\n"
  },
  {
    "path": "addons/guide/ui/guide_icon_renderer.gd",
    "content": "## Base class for icon renderers. Note that all icon renderers must be tool\n## scripts.\n@tool\nclass_name GUIDEIconRenderer\nextends Control\n\nconst Utils = preload(\"../editor/utils.gd\")\n\n## The priority of this icon renderer. Built-in renderers use priority 0, except the\n## controller renderer which uses -10. Built-in fallback renderer uses priority 100. \n## The smaller the number the higher the priority.\n@export var priority:int = 0\n\n## Whether or not this renderer can render an icon for this input.\nfunc supports(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> bool:\n\treturn false\n\t\n## Set up the scene so that the given input can be rendered. This will\n## only be called for input where `supports` has returned true.\nfunc render(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> void:\n\tpass\n \n\n## A cache key for the given input. This should be unique for this renderer\n## and the given input. The same input and options should yield the same cache key for\n## each renderer.\nfunc cache_key(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> String:\n\tpush_error(\"Custom renderers must override the cache_key function to ensure proper caching.\")\n\treturn \"i-forgot-the-cache-key\"\n\nfunc _ready():\n\t## Do not mess with the process mode in edited scenes. \n\tif Utils.is_node_in_edited_scene(self):\n\t\treturn\n\tprocess_mode = Node.PROCESS_MODE_ALWAYS\n\n"
  },
  {
    "path": "addons/guide/ui/guide_icon_renderer.gd.uid",
    "content": "uid://c25x75clb4fgl\n"
  },
  {
    "path": "addons/guide/ui/guide_input_formatter.gd",
    "content": "@tool\n## Helper class for formatting GUIDE input for the UI.\nclass_name GUIDEInputFormatter\n\nconst IconMaker = preload(\"icon_maker/icon_maker.gd\")\nconst KeyRenderer:PackedScene = preload(\"renderers/keyboard/guide_key_renderer.tscn\")\nconst MouseRenderer:PackedScene = preload(\"renderers/mouse/guide_mouse_renderer.tscn\")\nconst TouchRenderer:PackedScene = preload(\"renderers/touch/guide_touch_renderer.tscn\")\nconst JoyRenderer:PackedScene = preload(\"renderers/joy/guide_joy_renderer.tscn\")\nconst ControllerRenderer:PackedScene = preload(\"renderers/controllers/guide_controller_renderer.tscn\")\nconst ActionRenderer:PackedScene = preload(\"renderers/misc/guide_action_renderer.tscn\")\nconst FallbackRenderer:PackedScene = preload(\"renderers/misc/guide_fallback_renderer.tscn\")\nconst DefaultTextProvider = preload(\"text_providers/default_text_provider.gd\")\nconst ControllerTextProvider = preload(\"text_providers/controllers/guide_controller_text_provider.gd\")\n\n\n# These are shared across all instances\nstatic var _icon_maker:IconMaker\nstatic var _icon_renderers:Array[GUIDEIconRenderer] = []\nstatic var _text_providers:Array[GUIDETextProvider] = []\nstatic var _is_ready:bool = false\n\n\n\n## Separator to separate mixed input. \nstatic var mixed_input_separator:String = \", \"\n## Separator to separate chorded input.\nstatic var chorded_input_separator:String = \" + \"\n## Separator to separate combo input.\nstatic var combo_input_separator:String = \" > \"\n\n# These are per-instance\nvar _action_resolver:Callable\nvar _icon_size:int\n\n## The formatting options that this renderer uses. See [GUIDEInputFormattingOptions].\nvar formatting_options:GUIDEInputFormattingOptions = GUIDEInputFormattingOptions.new()\n\nstatic func _ensure_readiness() -> void:\n\tif _is_ready:\n\t\treturn\n\t\t\n\t# reconnect to an icon maker that might be there\n\tvar root = Engine.get_main_loop().root\t\n\tfor child in root.get_children():\n\t\tif child is IconMaker:\n\t\t\t_icon_maker = child\n\t\t\t\n\tif _icon_maker == null:\n\t\t_icon_maker = preload(\"icon_maker/icon_maker.tscn\").instantiate()\n\t\troot.add_child.call_deferred(_icon_maker)\n\t\n\tadd_icon_renderer(KeyRenderer.instantiate())\n\tadd_icon_renderer(MouseRenderer.instantiate())\n\tadd_icon_renderer(TouchRenderer.instantiate())\n\tadd_icon_renderer(ActionRenderer.instantiate())\n\tadd_icon_renderer(JoyRenderer.instantiate())\n\tadd_icon_renderer(ControllerRenderer.instantiate())\n\tadd_icon_renderer(FallbackRenderer.instantiate())\n\t\n\tadd_text_provider(DefaultTextProvider.new())\n\tadd_text_provider(ControllerTextProvider.new())\n\t\n\t_is_ready = true\n\n\n## This will clean up the rendering infrastructure used for generating \n## icons. Note that in a normal game you will have no need to call this\n## as the infrastructure is needed throughout the run of your game.\n## It might be useful in tests though, to get rid of spurious warnings\n## about orphaned nodes.\nstatic func cleanup():\n\t_is_ready = false\n\t\t\n\t# free all the nodes to avoid memory leaks\n\tfor renderer in _icon_renderers:\n\t\trenderer.queue_free()\n\t\t\n\t_icon_renderers.clear()\n\t\n\t_text_providers.clear()\n\tif is_instance_valid(_icon_maker):\n\t\t_icon_maker.queue_free()\n\n\nfunc _init(icon_size:int = 32, resolver:Callable = func(action) -> GUIDEActionMapping: return null ):\n\t_icon_size = icon_size\n\t_action_resolver = resolver\n\n\n## Adds an icon renderer for rendering icons.\nstatic func add_icon_renderer(renderer:GUIDEIconRenderer) -> void:\n\t_icon_renderers.append(renderer)\n\t_icon_renderers.sort_custom(func(r1, r2): return r1.priority < r2.priority)\n\t\n## Removes an icon renderer.\nstatic func remove_icon_renderer(renderer:GUIDEIconRenderer) -> void:\n\t_icon_renderers.erase(renderer)\n\t\n## Adds a text provider for rendering text.\nstatic func add_text_provider(provider:GUIDETextProvider) -> void:\n\t_text_providers.append(provider)\n\t_text_providers.sort_custom(func(r1, r2): return r1.priority < r2.priority)\n\n\t\n## Removes a text provider\t\nstatic func remove_text_provider(provider:GUIDETextProvider) -> void:\n\t_text_providers.erase(provider)\n\n\n## Returns an input formatter that can format actions using the currently active inputs.\nstatic func for_active_contexts(icon_size:int = 32) -> GUIDEInputFormatter:\n\tvar resolver := func(action:GUIDEAction) -> GUIDEActionMapping:\n\t\tfor mapping in GUIDE._active_action_mappings:\n\t\t\tif mapping.action == action:\n\t\t\t\treturn mapping\n\t\treturn null\n\treturn GUIDEInputFormatter.new(icon_size, resolver)\n\n\n## Returns an input formatter that can format actions using the given context.\nstatic func for_context(context:GUIDEMappingContext, icon_size:int = 32) -> GUIDEInputFormatter:\n\tvar resolver:Callable = func(action:GUIDEAction) -> GUIDEActionMapping:\n\t\tfor mapping in context.mappings:\n\t\t\tif mapping.action == action:\n\t\t\t\treturn  mapping\n\t\treturn null\n\t\t\n\treturn GUIDEInputFormatter.new(icon_size, resolver)\n\n## Returns an input formatter that can format actions using the given contexts.\nstatic func for_contexts(contexts:Array[GUIDEMappingContext], icon_size:int = 32) -> GUIDEInputFormatter:\n\tvar resolver:Callable = func(action:GUIDEAction) -> GUIDEActionMapping:\n\t\tfor context in contexts:\n\t\t\tfor mapping in context.mappings:\n\t\t\t\tif mapping.action == action:\n\t\t\t\t\treturn  mapping\n\t\treturn null\n\t\t\n\treturn GUIDEInputFormatter.new(icon_size, resolver)\t\n\t\n\t\n## Formats the action input as richtext with icons suitable for a RichTextLabel. This function\n## is async as icons may need to be rendered in the background which can take a few frames, so \n## you will need to await on it.\nfunc action_as_richtext_async(action:GUIDEAction) -> String:\n\treturn await _materialized_as_richtext_async(_materialize_action_input(action))\n\n\n## Formats the action input as plain text which can be used in any UI component. This is a bit\n## more light-weight than formatting as icons and returns immediately.\nfunc action_as_text(action:GUIDEAction) -> String:\n\treturn _materialized_as_text(_materialize_action_input(action))\n\n## Formats the input as richtext with icons suitable for a RichTextLabel. This function\n## is async as icons may need to be rendered in the background which can take a few frames, so \n## you will need to await on it.\nfunc input_as_richtext_async(input:GUIDEInput, materialize_actions:bool = true) -> String:\n\treturn await _materialized_as_richtext_async( \n\t\t_materialize_input(FormattingContext.for_input(input), materialize_actions) \n\t)\n\n\n## Formats the input as plain text which can be used in any UI component. This is a bit\n## more light-weight than formatting as icons and returns immediately.\nfunc input_as_text(input:GUIDEInput, materialize_actions:bool = true) -> String:\n\treturn _materialized_as_text(\n\t\t_materialize_input(FormattingContext.for_input(input), materialize_actions)\n\t)\t\n\t\n\n## Renders materialized input as text.\nfunc _materialized_as_text(input:MaterializedInput) -> String:\n\t_ensure_readiness()\n\tif input is MaterializedSimpleInput:\n\t\tvar text:String = \"\"\n\t\tfor provider in _text_providers:\n\t\t\tif provider.supports(input.input, formatting_options):\n\t\t\t\ttext = provider.get_text(input.input, formatting_options)\n\t\t\t\t# first provider wins\n\t\t\t\tbreak\n\t\tif text == \"\":\n\t\t\tpass\n\t\t\t## push_warning(\"No formatter found for input \", input)\n\t\treturn text\n\n\tvar separator := _separator_for_input(input)\n\tif separator == \"\" or input.parts.is_empty():\n\t\treturn \"\"\n\t\t\n\tvar parts:Array[String] = []\n\tfor part in input.parts:\n\t\tvar result := _materialized_as_text(part)\n\t\t# strip out empty parts (e.g. from devices we ignore)\n\t\tif not result.is_empty(): \n\t\t\tparts.append(result)\t\n\t\t\n\treturn separator.join(parts)\n\t\t\t\n## Renders materialized input as rich text.\nfunc _materialized_as_richtext_async(input:MaterializedInput) -> String:\n\t_ensure_readiness()\t\n\tif input is MaterializedSimpleInput:\n\t\tvar icon:Texture2D = null\n\t\tfor renderer:GUIDEIconRenderer in _icon_renderers:\n\t\t\tif renderer.supports(input.input, formatting_options):\n\t\t\t\ticon = await _icon_maker.make_icon(input.input, renderer, _icon_size, formatting_options)\n\t\t\t\t# first renderer wins\n\t\t\t\tbreak\n\t\tif icon == null:\n\t\t\tpush_warning(\"No renderer found for input \", input)\n\t\t\treturn \"\"\n\t\t\n\t\treturn \"[img]%s[/img]\" % [icon.resource_path]\n\t\n\n\tvar separator := _separator_for_input(input)\n\tif separator == \"\" or input.parts.is_empty():\n\t\treturn \"\"\n\t\t\n\tvar parts:Array[String] = []\n\tfor part in input.parts:\n\t\tvar result := await _materialized_as_richtext_async(part)\n\t\t# strip out any empty part (e.g. from devices we skip)\n\t\tif not result.is_empty():\n\t\t\tparts.append(result)\t\n\t\t\n\treturn separator.join(parts)\n\t\t\n\nfunc _separator_for_input(input:MaterializedInput) -> String:\n\tif input is MaterializedMixedInput:\n\t\treturn mixed_input_separator\n\telif input is MaterializedComboInput:\n\t\treturn combo_input_separator\n\telif input is MaterializedChordedInput:\n\t\treturn chorded_input_separator\n\n\tpush_error(\"Unknown materialized input type\")\n\treturn \"\"\n\t\t\t\n\n## Materializes action input.\t\nfunc _materialize_action_input(action:GUIDEAction) -> MaterializedInput:\n\tvar result := MaterializedMixedInput.new()\n\tif action == null:\n\t\tpush_warning(\"Trying to get inputs for a null action.\")\n\t\treturn result\n\t\n\t# get the mapping for this action\n\tvar mapping:GUIDEActionMapping = _action_resolver.call(action)\n\t\n\t# if we have no mapping, well that's it, return an empty mixed input\n\tif mapping == null:\n\t\treturn result\n\t\t\n\t# collect input mappings\n\tfor input_mapping in mapping.input_mappings:\n\t\tvar chorded_actions:Array[MaterializedInput] = []\n\t\tvar combos:Array[MaterializedInput] = []\n\t\t\n\t\tfor trigger in input_mapping.triggers:\n\t\t\t# if we have a combo trigger, materialize its input.\n\t\t\tif trigger is GUIDETriggerCombo:\n\t\t\t\tvar combo := MaterializedComboInput.new()\n\t\t\t\tfor step:GUIDETriggerComboStep in trigger.steps:\n\t\t\t\t\tcombo.parts.append(_materialize_action_input(step.action))\n\t\t\t\tcombos.append(combo)\n\n\t\t\t# if we have a chorded action, materialize its input\n\t\t\tif trigger is GUIDETriggerChordedAction:\n\t\t\t\tchorded_actions.append(_materialize_action_input(trigger.action))\n\n\t\tif not chorded_actions.is_empty():\n\t\t\t# if we have chorded action then the whole mapping is chorded.\n\t\t\tvar chord := MaterializedChordedInput.new()\n\t\t\tfor chorded_action in chorded_actions:\n\t\t\t\tchord.parts.append(chorded_action)\n\t\t\tfor combo in combos:\n\t\t\t\tchord.parts.append(combo)\n\t\t\tif combos.is_empty():\n\t\t\t\tif input_mapping.input != null:\n\t\t\t\t\tvar additional_inputs := _materialize_input(FormattingContext.for_action(input_mapping.input, input_mapping, action))\n\t\t\t\t\t# https://github.com/godotneers/G.U.I.D.E/issues/175\n\t\t\t\t\t# additional inputs can be blank if they are filtered out. if they are blank\n\t\t\t\t\t# we can discard the whole input mapping, as it could never trigger (we have a chorded action + no input)\n\t\t\t\t\tif not additional_inputs.is_blank():\n\t\t\t\t\t\tchord.parts.append(additional_inputs)\t\t\t\t\t\n\t\t\t\t\t\tresult.parts.append(chord)\n\t\telse:\n\t\t\tfor combo in combos:\n\t\t\t\tresult.parts.append(combo)\n\t\t\tif combos.is_empty():\n\t\t\t\tif input_mapping.input != null:\n\t\t\t\t\tresult.parts.append(\n\t\t\t\t\t\t_materialize_input(FormattingContext.for_action(input_mapping.input, input_mapping, action))\n\t\t\t\t\t)\n\treturn result\n\t\n## Materializes direct input.\nfunc _materialize_input(context:FormattingContext, materialize_actions:bool = true) -> MaterializedInput:\n\tif context.input == null:\n\t\tpush_warning(\"Trying to materialize a null input.\")\n\t\treturn MaterializedMixedInput.new()\n\t\n\t# if the formatting options exclude this input, return an empty input.\n\tif not formatting_options.input_filter.call(context):\n\t\treturn MaterializedMixedInput.new()\n\t\t\n\t\n\t# if its an action input, get its parts\n\tif context.input is GUIDEInputAction:\n\t\tif materialize_actions:\n\t\t\treturn _materialize_action_input(context.input.action)\n\t\telse:\n\t\t\treturn MaterializedSimpleInput.new(context.input)\n\t\n\t# if its a key input, split out the modifiers\n\tif context.input is GUIDEInputKey:\n\t\tvar chord := MaterializedChordedInput.new()\n\t\tif context.input.control:\n\t\t\tvar ctrl := GUIDEInputKey.new()\n\t\t\tctrl.key = KEY_CTRL\n\t\t\tchord.parts.append(MaterializedSimpleInput.new(ctrl))\n\t\tif context.input.alt:\n\t\t\tvar alt := GUIDEInputKey.new()\n\t\t\talt.key = KEY_ALT\n\t\t\tchord.parts.append(MaterializedSimpleInput.new(alt))\n\t\tif context.input.shift:\n\t\t\tvar shift := GUIDEInputKey.new()\n\t\t\tshift.key = KEY_SHIFT\n\t\t\tchord.parts.append(MaterializedSimpleInput.new(shift))\n\t\tif context.input.meta:\n\t\t\tvar meta := GUIDEInputKey.new()\n\t\t\tmeta.key = KEY_META\n\t\t\tchord.parts.append(MaterializedSimpleInput.new(meta))\n\t\n\t\t# got no modifiers?\n\t\tif chord.parts.is_empty():\n\t\t\treturn MaterializedSimpleInput.new(context.input)\n\t\t\t\n\t\tchord.parts.append(MaterializedSimpleInput.new(context.input))\t\n\t\treturn chord\n\n\t# everything else is just a simple input\n\treturn MaterializedSimpleInput.new(context.input)\n\t\n\t\nclass MaterializedInput:\n\tfunc is_blank() -> bool:\n\t\treturn false\n\t\nclass MaterializedSimpleInput:\n\textends MaterializedInput\n\tvar input:GUIDEInput\t\n\t\n\tfunc _init(input:GUIDEInput):\n\t\tself.input = input\n\t\t\n\tfunc _to_string() -> String:\n\t\treturn \"MaterializedSimpleInput(%s)\" % input\n\t\nclass MaterializedMixedInput:\n\textends MaterializedInput\n\tvar parts:Array[MaterializedInput] = []\n\t\n\tfunc is_blank() -> bool:\n\t\treturn parts.is_empty()\n\t\n\tfunc _to_string() -> String:\n\t\treturn \"MaterializedMixedInput(%s)\" % \", \".join(parts.map(func(it:MaterializedInput)->String: return it.to_string()))\n\t\nclass MaterializedChordedInput:\n\textends MaterializedInput\n\tvar parts:Array[MaterializedInput] = []\n\n\tfunc is_blank() -> bool:\n\t\treturn parts.is_empty()\n\n\tfunc _to_string() -> String:\n\t\treturn \"MaterializedChordedInput(%s)\" % \", \".join(parts.map(func(it:MaterializedInput)->String: return it.to_string()))\n\t\nclass MaterializedComboInput:\n\textends MaterializedInput\n\tvar parts:Array[MaterializedInput] = []\n\n\tfunc is_blank() -> bool:\n\t\treturn parts.is_empty()\n\n\tfunc _to_string() -> String:\n\t\treturn \"MaterializedComboInput(%s)\" % \", \".join(parts.map(func(it:MaterializedInput)->String: return it.to_string()))\n\t\n\n## A formatting context.\nclass FormattingContext:\n\t## The input we'd like to format.\n\tvar input:GUIDEInput\n\t## The input mapping from which this input originates.\n\t## Can be null, if this is a raw input.\n\tvar input_mapping:GUIDEInputMapping\n\t## The action to which the input mapping is bound.\n\t## Can be null, if there is no input mapping. \n\tvar action:GUIDEAction\n\t\n\tstatic func for_input(input:GUIDEInput) -> FormattingContext:\n\t\tvar result = FormattingContext.new()\n\t\tresult.input = input\n\t\treturn result\n\t\t\n\tstatic func for_action(input:GUIDEInput, input_mapping:GUIDEInputMapping, action:GUIDEAction) -> FormattingContext:\n\t\tvar result = FormattingContext.new()\n\t\tresult.input = input\n\t\tresult.input_mapping = input_mapping\n\t\tresult.action = action\n\t\treturn result\n\t\t\n"
  },
  {
    "path": "addons/guide/ui/guide_input_formatter.gd.uid",
    "content": "uid://duisgvlqywtbn\n"
  },
  {
    "path": "addons/guide/ui/guide_input_formatting_options.gd",
    "content": "class_name GUIDEInputFormattingOptions\n\n## Options for rendering joy icons. \nenum JoyRendering {\n\t## Renders by detecting the joy type and uses the appropriate icon set for this joy type.  \n\tDEFAULT = 0,\n\t\n\t## Renders by detecting the joy type but uses the preferred joy type, if it cannot be detected. \n\tPREFER_JOY_TYPE = 1,\n\t\n\t## Always renders joy input using the preferred joy type, no matter which type is detected.\n\tFORCE_JOY_TYPE = 2,\n}\n\n## Supported joy types\nenum JoyType {\n\t## Used for joysticks which are not controllers or as a fallback if no controller\n\t## type can be determined.\n\tGENERIC_JOY = 0,\n\t## Used for Microsoft controllers (e.g. XBox). \n\tMICROSOFT_CONTROLLER = 1,\n\t## Used for Nintendo controllers (e.g. Switch). \n\tNINTENDO_CONTROLLER = 2,\n\t## Used for Sony controllers (e.g. PlayStation). \n\tSONY_CONTROLLER = 3,\n\t## Used for Steam Deck controllers\n\tSTEAM_DECK_CONTROLLER = 4,\n}\n\n## An input filter that shows all input. This is the default.\nstatic var INPUT_FILTER_SHOW_ALL:Callable:\n\t# should be a const but Callables cannot be const, so we use a read-only static property.\n\t# Note: this is Variant here to avoid circular dependencies between GUIDEInputFormatter\n\t# and GUIDEInputFormattingOptions. Godot really doesn't like these.\n\tget: return func(_context:Variant) -> bool: return true\n\n## A callable that allows for filtering which parts of an input are included in the\n## formatted output. The callable takes a formatting context:\n## [codeblock]\n## options.input_filter = func(context:GUIDEInputFormatter.FormattingContext) -> bool:\n##      # only show keyboard input\n##     return context.input.device_type & GUIDEInput.DeviceType.KEYBOARD > 0\n## [/codeblock]\n## If the function returns true, then the given input will be shown in the formatted\n## output, otherwise it will be ignored.\nvar input_filter:Callable = INPUT_FILTER_SHOW_ALL\n\n## Determines how joy icons are rendered. \nvar joy_rendering:JoyRendering = JoyRendering.DEFAULT\n\n## The preferred Joy type for rendering Joy input. This setting only has effect if the \n## joy_rendering setting is set to something different than [code]DEFAULT[/code].  \nvar preferred_joy_type:JoyType = JoyType.GENERIC_JOY\n\n"
  },
  {
    "path": "addons/guide/ui/guide_input_formatting_options.gd.uid",
    "content": "uid://l5qsnxd0tc7o\n"
  },
  {
    "path": "addons/guide/ui/guide_text_provider.gd",
    "content": "## Base class for text providers. A text provider provides a textual representation\n## of an input which is displayed to the user.\n## scripts.\n@tool\nclass_name GUIDETextProvider\n\n## The priority of this text provider. The built-in text provider uses priority 0.\n## The smaller the number the higher the priority.\n@export var priority:int = 0\n\n## Whether or not this provider can provide a text for this input.\nfunc supports(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> bool:\n\treturn false\n\t\n## Provides the text for the given input. Will only be called when the \n## input is supported by this text provider. Note that for key input\n## this is not supposed to look at the modifiers. This function will\n## be called separately for each modifier.\nfunc get_text(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> String:\n\treturn \"not implemented\"\n \n\n"
  },
  {
    "path": "addons/guide/ui/guide_text_provider.gd.uid",
    "content": "uid://di8xw2ysyq483\n"
  },
  {
    "path": "addons/guide/ui/icon_maker/icon_maker.gd",
    "content": "@tool\nextends Node\n\nconst CACHE_DIR:String = \"user://_guide_cache\"\n\n@onready var _sub_viewport:SubViewport = %SubViewport\n@onready var _root:Node2D = %Root\n@onready var _scene_holder:Sprite2D = %SceneHolder\n\nvar _pending_requests:Array[Job] = []\nvar _current_request:Job = null\nvar _fetch_image:bool = false\n\nfunc _ready() -> void:\n\t# keep working when game is paused\n\tprocess_mode = Node.PROCESS_MODE_ALWAYS\n\t# don't needlessly eat performance\n\tif _pending_requests.is_empty():\n\t\tset_process(false)\n\n\t\nfunc clear_cache() -> void:\n\tvar files := DirAccess.get_files_at(CACHE_DIR)\n\tfor file in files:\n\t\tDirAccess.remove_absolute(CACHE_DIR + \"/\" + file)\n\n## Makes an icon for the given input and returns a Texture2D with the icon. Icons\n## are cached on disk so subsequent calls for the same input will be faster.\nfunc make_icon(input:GUIDEInput, renderer:GUIDEIconRenderer, \\\n\t\theight_px:int, options:GUIDEInputFormattingOptions) -> Texture2D:\n\tDirAccess.make_dir_recursive_absolute(CACHE_DIR)\n\tvar cache_key := (str(height_px) + renderer.cache_key(input, options)).sha256_text()\n\tvar cache_path := \"user://_guide_cache/\" + cache_key + \".res\"\n\tif ResourceLoader.exists(cache_path):\n\t\treturn ResourceLoader.load(cache_path, \"Texture2D\") \n\t\n\tvar job := Job.new()\n\tjob.height = height_px\n\tjob.input = input\n\tjob.options = options\n\tjob.renderer = renderer\n\t_pending_requests.append(job)\n\tset_process(true)\n\t\n\tawait job.done\n\t\n\tvar image_texture := ImageTexture.create_from_image(job.result)\n\tResourceSaver.save(image_texture, cache_path)\n\timage_texture.take_over_path(cache_path)\n\t\n\treturn image_texture\n\t\t\n\t\n\nfunc _process(_delta:float) -> void:\n\tif _current_request == null and _pending_requests.is_empty():\n\t\t# nothing more to do..\n\t\tset_process(false)\n\t\treturn \n\t\t\n\t# nothing in progress, so pick the next request\n\tif _current_request == null:\n\t\t_current_request = _pending_requests.pop_front()\n\t\tvar renderer := _current_request.renderer\n\t\t_root.add_child(renderer)\n\t\t\n\t\trenderer.render(_current_request.input, _current_request.options)\n\t\tawait get_tree().process_frame\n\t\t\n\t\tvar actual_size := renderer.get_rect().size\n\t\tvar scale := float(_current_request.height) / float(actual_size.y)\n\t\t_root.scale = Vector2.ONE * scale\n\t\t_sub_viewport.size = actual_size  * scale\n\t\t\t\n\t\t_sub_viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS\n\t\t\t\n\t\t# give the renderer some time to update itself. 3 frames seem\n\t\t# to work nicely and keep things speedy.\t\n\t\tawait get_tree().process_frame\n\t\tawait get_tree().process_frame\n\t\tawait get_tree().process_frame\n\t\t\t\n\t\t_fetch_image = true\n\t\treturn\n\t\t\n\t# fetch the image after the renderer is done\t\n\tif _fetch_image:\n\t\t# we're done. make a copy of the viewport texture\n\t\tvar image := _scene_holder.texture.get_image()\n\t\t_current_request.result = image\n\t\t_current_request.done.emit()\n\t\t_current_request = null\n\t\t# remove the renderer\n\t\t_root.remove_child(_root.get_child(0))\n\t\t_sub_viewport.render_target_update_mode = SubViewport.UPDATE_DISABLED\n\t\t_fetch_image = false\t\t\t\n\t\t\nclass Job:\n\tsignal done()\n\tvar height:int\n\tvar input:GUIDEInput\n\tvar renderer:GUIDEIconRenderer\n\tvar options:GUIDEInputFormattingOptions\n\tvar result:Image\n\t\n\t\n"
  },
  {
    "path": "addons/guide/ui/icon_maker/icon_maker.gd.uid",
    "content": "uid://dq6cdbdturmel\n"
  },
  {
    "path": "addons/guide/ui/icon_maker/icon_maker.tscn",
    "content": "[gd_scene load_steps=3 format=3 uid=\"uid://8thurteeibtu\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/ui/icon_maker/icon_maker.gd\" id=\"1_hdbjk\"]\n\n[sub_resource type=\"ViewportTexture\" id=\"ViewportTexture_kra7t\"]\nviewport_path = NodePath(\"SubViewport\")\n\n[node name=\"GUIDEIconMaker\" type=\"Node2D\"]\nscript = ExtResource(\"1_hdbjk\")\n\n[node name=\"SubViewport\" type=\"SubViewport\" parent=\".\"]\nunique_name_in_owner = true\ntransparent_bg = true\ngui_disable_input = true\ngui_snap_controls_to_pixels = false\n\n[node name=\"Root\" type=\"Node2D\" parent=\"SubViewport\"]\nunique_name_in_owner = true\nscale = Vector2(0.1, 0.1)\n\n[node name=\"SceneHolder\" type=\"Sprite2D\" parent=\".\"]\nunique_name_in_owner = true\nvisible = false\ntexture = SubResource(\"ViewportTexture_kra7t\")\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/guide_controller_render_style.gd",
    "content": "@tool\n@icon(\"../style.svg\")\n## This is style used by the controller renderer. \nclass_name GUIDEControllerRenderStyle\nextends Resource\n\n## Texture for the bottom action button: Sony Cross, Xbox A, Nintendo B \n@export var a_button:Texture2D\n## Texture for the right action button: Sony Circle, Xbox B, Nintendo A.\n@export var b_button:Texture2D\n## Texture for the left action button: Sony Square, Xbox X, Nintendo Y.\n@export var x_button:Texture2D\n## Texture for the top action button: Sony Triangle, Xbox Y, Nintendo X.\n@export var y_button:Texture2D\n## Texture for the left stick of the controller. \n@export var left_stick:Texture2D\n## Texture for the left stick click button of the controller. \n@export var left_stick_click:Texture2D\n## Texture for the right stick of the controller. \n@export var right_stick:Texture2D\n## Texture for the right stick click button of the controller. \n@export var right_stick_click:Texture2D\n## Texture for the  left shoulder button: Sony L1, Xbox LB button.\n@export var left_bumper:Texture2D\n## Texture for the right shoulder button: Sony R1, XBOX RB button.\n@export var right_bumper:Texture2D\n## Texture for the left trigger. \n@export var left_trigger:Texture2D\n## Texture for the right trigger. \n@export var right_trigger:Texture2D\n## Texture for the up direction on the directional pad. \n@export var dpad_up:Texture2D\n## Texture for the left direction on the directional pad. \n@export var dpad_left:Texture2D\n## Texture for the right direction on the directional pad. \n@export var dpad_right:Texture2D\n## Texture for the down direction on the directional pad. \n@export var dpad_down:Texture2D\n## Texture for the Start button: Sony Options, Xbox Menu, Nintendo + button \n@export var start:Texture2D\n## Texture for the Miscellaneous 1 button: Xbox share , PS5 microphone , Nintendo Switch capture.\n@export var misc1:Texture2D\n## Texture for the back button:  Sony Select, Xbox Back, Nintendo - button\n@export var back:Texture2D\n## Texture for the touchpad.\n@export var touch_pad:Texture2D\n\n\n@export_category(\"Directions\")\n## An icon indicating horizontal movement.\n@export var horizontal:Texture2D\n## An icon indicating vertical movement.\n@export var vertical:Texture2D\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/guide_controller_render_style.gd.uid",
    "content": "uid://dwrjpecsnhu6l\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/guide_controller_renderer.gd",
    "content": "@tool\nclass_name GUIDEControllerRenderer\nextends GUIDEIconRenderer\n\nconst GUIDEFormattingUtils = preload(\"../../guide_formatting_utils.gd\")\nconst ControllerType = GUIDEFormattingUtils.ControllerType\n\n## Icon sets to be used for the controller types. The key is the controller type, the value is an \n## icon set that should be used. \nstatic var _controller_styles:Dictionary = {\n\tControllerType.MICROSOFT: preload(\"styles/microsoft/microsoft.tres\"),\n\tControllerType.NINTENDO: preload(\"styles/nintendo/nintendo.tres\"),\n\tControllerType.SONY: preload(\"styles/sony/sony.tres\"),\n\tControllerType.STEAM_DECK: preload(\"styles/steam_deck/steam_deck.tres\"),\n}\n\n@onready var _a_button:TextureRect = %AButton\n@onready var _b_button:TextureRect = %BButton\n@onready var _x_button:TextureRect = %XButton\n@onready var _y_button:TextureRect = %YButton\n@onready var _left_stick:TextureRect = %LeftStick\n@onready var _left_stick_click:TextureRect = %LeftStickClick\n@onready var _right_stick:TextureRect = %RightStick\n@onready var _right_stick_click:TextureRect = %RightStickClick\n@onready var _left_bumper:Control = %LeftBumper\n@onready var _right_bumper:Control = %RightBumper\n@onready var _left_trigger:Control = %LeftTrigger\n@onready var _right_trigger:TextureRect = %RightTrigger\n@onready var _dpad_up:TextureRect = %DpadUp\n@onready var _dpad_left:TextureRect = %DpadLeft\n@onready var _dpad_right:TextureRect = %DpadRight\n@onready var _dpad_down:TextureRect = %DpadDown\n@onready var _start:TextureRect = %Start\n@onready var _misc1:TextureRect = %Misc1\n@onready var _back:TextureRect = %Back\n@onready var _left_right:TextureRect = %LeftRight\n@onready var _up_down:TextureRect = %UpDown\n@onready var _controls:Control = %Controls\n@onready var _directions:Control = %Directions\n@onready var _touch_pad:TextureRect = %TouchPad\n\n\nfunc _init():\n\tpriority = -10\n\nfunc _ready():\n\tsuper()\n\t\n## Sets the style that is used for rendering the given controller type. \t\nstatic func set_style(type:ControllerType, style:GUIDEControllerRenderStyle) -> void:\n\tif not is_instance_valid(style):\n\t\tpush_error(\"Icon set must not be null.\")\n\t\treturn\n\t\n\tif type == ControllerType.UNKNOWN:\n\t\tpush_error(\"Cannot set icon set for the unknown controller. Use GUIDEInputFormattingOptions to set up how unknown controllers are rendered.\")\t\n\t\treturn\n\t\t\n\t_controller_styles[type] = style\n\n\t\n## Sets up the textures for the given controller type.\nfunc _setup_textures(type:ControllerType) -> void:\n\tif type == ControllerType.UNKNOWN:\n\t\tpush_error(\"Tried to set up textures with unknown controller type. This is a bug, please report it.\")\n\t\treturn\n\t\t\n\tvar style:GUIDEControllerRenderStyle = _controller_styles[type]\n\t\n\t_a_button.texture = style.a_button \n\t_b_button.texture = style.b_button \n\t_x_button.texture = style.x_button \n\t_y_button.texture = style.y_button \n\t_left_stick.texture = style.left_stick \n\t_left_stick_click.texture = style.left_stick_click \n\t_right_stick.texture = style.right_stick \n\t_right_stick_click.texture = style.right_stick_click \n\t_left_bumper.texture = style.left_bumper \n\t_right_bumper.texture = style.right_bumper \n\t_left_trigger.texture = style.left_trigger \n\t_right_trigger.texture = style.right_trigger \n\t_dpad_up.texture = style.dpad_up \n\t_dpad_left.texture = style.dpad_left \n\t_dpad_right.texture = style.dpad_right \n\t_dpad_down.texture = style.dpad_down \n\t_start.texture = style.start \n\t_misc1.texture = style.misc1 \n\t_back.texture = style.back \n\t_touch_pad.texture = style.touch_pad\n\t_left_right.texture = style.horizontal\n\t_up_down.texture = style.vertical\n\n\nfunc supports(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> bool:\n\t# we support rendering if the input effectively can be mapped to any known\n\t# controller type using the options\n\treturn GUIDEFormattingUtils.effective_controller_type(input, options) != ControllerType.UNKNOWN\n\t\n\n\t\t\nfunc render(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> void:\n\t# get effective controller type to use the correct textures.\n\tvar controller_type := GUIDEFormattingUtils.effective_controller_type(input, options)\n\t_setup_textures(controller_type)\n\t\n\tfor control in _controls.get_children():\n\t\tcontrol.visible = false\n\tfor direction in _directions.get_children():\n\t\tdirection.visible = false\n\t_directions.visible = false\n\t\t\n\t\n\tif input is GUIDEInputJoyAxis1D:\n\t\tmatch input.axis:\n\t\t\tJOY_AXIS_LEFT_X:\n\t\t\t\t_left_stick.visible = true\n\t\t\t\t_show_left_right()\n\t\t\tJOY_AXIS_LEFT_Y:\n\t\t\t\t_left_stick.visible = true\n\t\t\t\t_show_up_down()\n\t\t\tJOY_AXIS_RIGHT_X:\n\t\t\t\t_right_stick.visible = true\n\t\t\t\t_show_left_right()\n\t\t\tJOY_AXIS_RIGHT_Y:\n\t\t\t\t_right_stick.visible = true\n\t\t\t\t_show_up_down()\n\t\t\tJOY_AXIS_TRIGGER_LEFT:\n\t\t\t\t_left_trigger.visible = true\n\t\t\tJOY_AXIS_TRIGGER_RIGHT:\n\t\t\t\t_right_trigger.visible = true\n\t\n\tif input is GUIDEInputJoyAxis2D:\n\t\t# We assume that there is no input mixing horizontal and vertical\n\t\t# from different sticks into a 2D axis as this would confuse the \n\t\t# players. \n\t\tmatch input.x:\n\t\t\tJOY_AXIS_LEFT_X, JOY_AXIS_LEFT_Y:\n\t\t\t\t_left_stick.visible = true\n\t\t\tJOY_AXIS_RIGHT_X, JOY_AXIS_RIGHT_Y:\n\t\t\t\t_right_stick.visible = true\n\t\t\t\t\n\tif input is GUIDEInputJoyButton:\n\t\tmatch input.button:\n\t\t\tJOY_BUTTON_A:\n\t\t\t\t_a_button.visible = true\n\t\t\tJOY_BUTTON_B:\n\t\t\t\t_b_button.visible = true\n\t\t\tJOY_BUTTON_X:\n\t\t\t\t_x_button.visible = true\n\t\t\tJOY_BUTTON_Y:\n\t\t\t\t_y_button.visible = true\n\t\t\tJOY_BUTTON_DPAD_LEFT:\n\t\t\t\t_dpad_left.visible = true\n\t\t\tJOY_BUTTON_DPAD_RIGHT:\n\t\t\t\t_dpad_right.visible = true\n\t\t\tJOY_BUTTON_DPAD_UP:\n\t\t\t\t_dpad_up.visible = true\n\t\t\tJOY_BUTTON_DPAD_DOWN:\n\t\t\t\t_dpad_down.visible = true\n\t\t\tJOY_BUTTON_LEFT_SHOULDER:\n\t\t\t\t_left_bumper.visible = true\n\t\t\tJOY_BUTTON_RIGHT_SHOULDER:\n\t\t\t\t_right_bumper.visible = true\n\t\t\tJOY_BUTTON_LEFT_STICK:\n\t\t\t\t_left_stick_click.visible = true\n\t\t\tJOY_BUTTON_RIGHT_STICK:\n\t\t\t\t_right_stick_click.visible = true\n\t\t\tJOY_BUTTON_RIGHT_STICK:\n\t\t\t\t_right_stick_click.visible = true\n\t\t\tJOY_BUTTON_START:\n\t\t\t\t_start.visible = true\n\t\t\tJOY_BUTTON_BACK:\n\t\t\t\t_back.visible = true\n\t\t\tJOY_BUTTON_MISC1:\n\t\t\t\t_misc1.visible = true\n\t\t\tJOY_BUTTON_TOUCHPAD:\n\t\t\t\t_touch_pad.visible = true\n\t\t\t\t\t\n\tcall(\"queue_sort\")\t\t\n\nfunc _show_left_right():\n\t_directions.visible = true\n\t_left_right.visible = true\n\nfunc _show_up_down():\n\t_directions.visible = true\n\t_up_down.visible = true\n\t\n\nfunc cache_key(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> String:\n\tvar controller_type := GUIDEFormattingUtils.effective_controller_type(input, options)\n\tvar icon_set:GUIDEControllerRenderStyle = _controller_styles[controller_type]\n\t\n\treturn \"7581f483-bc68-411f-98ad-dc246fd2593a\" \\\n\t\t# output depends on the input to render\n\t\t+ input.to_string() \\\n\t\t# and the icon set we use to render it\n\t\t+ icon_set.resource_path\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/guide_controller_renderer.gd.uid",
    "content": "uid://1mjwnhjr65w4\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/guide_controller_renderer.tscn",
    "content": "[gd_scene load_steps=4 format=3 uid=\"uid://bsaylcb5ixjxk\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/ui/renderers/controllers/guide_controller_renderer.gd\" id=\"1_0s5v7\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bmgxqbypegjxh\" path=\"res://addons/guide/ui/renderers/textures/arrow_horizontal.svg\" id=\"2_vkejn\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bu5nlug6uf03w\" path=\"res://addons/guide/ui/renderers/textures/arrow_vertical.svg\" id=\"3_wqne5\"]\n\n[node name=\"ControllerRenderer\" type=\"MarginContainer\"]\nprocess_mode = 3\ncustom_minimum_size = Vector2(128, 128)\noffset_right = 100.0\noffset_bottom = 100.0\nsize_flags_horizontal = 0\nscript = ExtResource(\"1_0s5v7\")\npriority = -1\n\n[node name=\"Controls\" type=\"MarginContainer\" parent=\".\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntheme_override_constants/margin_left = 14\ntheme_override_constants/margin_top = 14\ntheme_override_constants/margin_right = 14\ntheme_override_constants/margin_bottom = 14\n\n[node name=\"AButton\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nlayout_mode = 2\nstretch_mode = 5\n\n[node name=\"BButton\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nlayout_mode = 2\nstretch_mode = 5\n\n[node name=\"XButton\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nlayout_mode = 2\nstretch_mode = 5\n\n[node name=\"YButton\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nlayout_mode = 2\nstretch_mode = 5\n\n[node name=\"LeftStick\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nlayout_mode = 2\nstretch_mode = 5\n\n[node name=\"LeftStickClick\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nlayout_mode = 2\nstretch_mode = 5\n\n[node name=\"RightStick\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nlayout_mode = 2\nstretch_mode = 5\n\n[node name=\"RightStickClick\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nlayout_mode = 2\nstretch_mode = 5\n\n[node name=\"LeftBumper\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nlayout_mode = 2\nstretch_mode = 5\n\n[node name=\"RightBumper\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nlayout_mode = 2\nstretch_mode = 5\n\n[node name=\"LeftTrigger\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nlayout_mode = 2\nstretch_mode = 5\n\n[node name=\"RightTrigger\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nlayout_mode = 2\nstretch_mode = 5\n\n[node name=\"DpadUp\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nlayout_mode = 2\nstretch_mode = 5\n\n[node name=\"DpadLeft\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nlayout_mode = 2\nstretch_mode = 5\n\n[node name=\"DpadRight\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nlayout_mode = 2\nstretch_mode = 5\n\n[node name=\"DpadDown\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nlayout_mode = 2\nstretch_mode = 5\n\n[node name=\"Start\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nlayout_mode = 2\nstretch_mode = 5\n\n[node name=\"Misc1\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nlayout_mode = 2\nstretch_mode = 5\n\n[node name=\"Back\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nlayout_mode = 2\nstretch_mode = 5\n\n[node name=\"TouchPad\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nlayout_mode = 2\nstretch_mode = 5\n\n[node name=\"Directions\" type=\"MarginContainer\" parent=\".\"]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\n\n[node name=\"LeftRight\" type=\"TextureRect\" parent=\"Directions\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntexture = ExtResource(\"2_vkejn\")\nstretch_mode = 5\n\n[node name=\"UpDown\" type=\"TextureRect\" parent=\"Directions\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntexture = ExtResource(\"3_wqne5\")\nstretch_mode = 5\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_A.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cujvw3m7aghgh\"\npath=\"res://.godot/imported/XboxSeriesX_A.png-0cf60b1ac665455ff01af7cf36cdb1ea.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_A.png\"\ndest_files=[\"res://.godot/imported/XboxSeriesX_A.png-0cf60b1ac665455ff01af7cf36cdb1ea.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_B.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://brgqhp87g40l5\"\npath=\"res://.godot/imported/XboxSeriesX_B.png-560ad7900a4c04843c792b8a0dd84ffd.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_B.png\"\ndest_files=[\"res://.godot/imported/XboxSeriesX_B.png-560ad7900a4c04843c792b8a0dd84ffd.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Dpad.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://j5kp5tnwtysq\"\npath=\"res://.godot/imported/XboxSeriesX_Dpad.png-c52f6a42d5850c0258037bce7ef49707.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Dpad.png\"\ndest_files=[\"res://.godot/imported/XboxSeriesX_Dpad.png-c52f6a42d5850c0258037bce7ef49707.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Dpad_Down.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://d1fgf8ia7q4vo\"\npath=\"res://.godot/imported/XboxSeriesX_Dpad_Down.png-9ba154783d769b4761a4a90377dfb7f2.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Dpad_Down.png\"\ndest_files=[\"res://.godot/imported/XboxSeriesX_Dpad_Down.png-9ba154783d769b4761a4a90377dfb7f2.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Dpad_Left.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bqtskbk6l8v7w\"\npath=\"res://.godot/imported/XboxSeriesX_Dpad_Left.png-9f4fc1e056c840521a87270d9621f635.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Dpad_Left.png\"\ndest_files=[\"res://.godot/imported/XboxSeriesX_Dpad_Left.png-9f4fc1e056c840521a87270d9621f635.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Dpad_Right.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://gsilbr1bntic\"\npath=\"res://.godot/imported/XboxSeriesX_Dpad_Right.png-00acb7b8c6d56c2533df09bae57aa2d2.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Dpad_Right.png\"\ndest_files=[\"res://.godot/imported/XboxSeriesX_Dpad_Right.png-00acb7b8c6d56c2533df09bae57aa2d2.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Dpad_Up.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://boc4an5lj12lp\"\npath=\"res://.godot/imported/XboxSeriesX_Dpad_Up.png-6ba144749714a99d31fb2bb763699da1.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Dpad_Up.png\"\ndest_files=[\"res://.godot/imported/XboxSeriesX_Dpad_Up.png-6ba144749714a99d31fb2bb763699da1.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_LB.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://uwqbgrkqv7qc\"\npath=\"res://.godot/imported/XboxSeriesX_LB.png-fd1c2bc3c18183d83fd848e5b7af4875.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_LB.png\"\ndest_files=[\"res://.godot/imported/XboxSeriesX_LB.png-fd1c2bc3c18183d83fd848e5b7af4875.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_LT.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://c1rw1hnlt3dlt\"\npath=\"res://.godot/imported/XboxSeriesX_LT.png-dba8ed2619568003c03317655d07e075.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_LT.png\"\ndest_files=[\"res://.godot/imported/XboxSeriesX_LT.png-dba8ed2619568003c03317655d07e075.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Left_Stick.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://wol4p4f32lfr\"\npath=\"res://.godot/imported/XboxSeriesX_Left_Stick.png-c101cd25abfc29cab9c4fa809f5c20b7.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Left_Stick.png\"\ndest_files=[\"res://.godot/imported/XboxSeriesX_Left_Stick.png-c101cd25abfc29cab9c4fa809f5c20b7.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Left_Stick_Click.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://c0xpc73ovry50\"\npath=\"res://.godot/imported/XboxSeriesX_Left_Stick_Click.png-8315a40e455a141a75cdd1c3f2607279.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Left_Stick_Click.png\"\ndest_files=[\"res://.godot/imported/XboxSeriesX_Left_Stick_Click.png-8315a40e455a141a75cdd1c3f2607279.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Menu.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://y70m84f40jkk\"\npath=\"res://.godot/imported/XboxSeriesX_Menu.png-9f2dfe3a34eceb8bded648b480d92d26.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Menu.png\"\ndest_files=[\"res://.godot/imported/XboxSeriesX_Menu.png-9f2dfe3a34eceb8bded648b480d92d26.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_RB.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://5j0mm7ydxc5b\"\npath=\"res://.godot/imported/XboxSeriesX_RB.png-f5cbabf9e905671e7db9b58b98eb6125.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_RB.png\"\ndest_files=[\"res://.godot/imported/XboxSeriesX_RB.png-f5cbabf9e905671e7db9b58b98eb6125.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_RT.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://dvsukgwjjn78x\"\npath=\"res://.godot/imported/XboxSeriesX_RT.png-bc51953c79e941e4f616e3e221b52f12.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_RT.png\"\ndest_files=[\"res://.godot/imported/XboxSeriesX_RT.png-bc51953c79e941e4f616e3e221b52f12.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Right_Stick.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://c1p7p5qpqxqfn\"\npath=\"res://.godot/imported/XboxSeriesX_Right_Stick.png-53b7d87496726eceac38f10e93dc4362.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Right_Stick.png\"\ndest_files=[\"res://.godot/imported/XboxSeriesX_Right_Stick.png-53b7d87496726eceac38f10e93dc4362.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Right_Stick_Click.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://pmymadotp1l0\"\npath=\"res://.godot/imported/XboxSeriesX_Right_Stick_Click.png-60c3449bffe44ec2e56b0c829f3f4b2f.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Right_Stick_Click.png\"\ndest_files=[\"res://.godot/imported/XboxSeriesX_Right_Stick_Click.png-60c3449bffe44ec2e56b0c829f3f4b2f.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Share.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://b8b2oomlnf5tt\"\npath=\"res://.godot/imported/XboxSeriesX_Share.png-bec2d036c0b6aea6af06e0a95067a60f.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Share.png\"\ndest_files=[\"res://.godot/imported/XboxSeriesX_Share.png-bec2d036c0b6aea6af06e0a95067a60f.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_View.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://b5wc83dex8igr\"\npath=\"res://.godot/imported/XboxSeriesX_View.png-a0c8c2902039ea43d89d170bc769bbb2.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_View.png\"\ndest_files=[\"res://.godot/imported/XboxSeriesX_View.png-a0c8c2902039ea43d89d170bc769bbb2.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_X.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://dqnryo1s6qi45\"\npath=\"res://.godot/imported/XboxSeriesX_X.png-5edc2dc53019ab9dfe4d3c7218b31e3f.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_X.png\"\ndest_files=[\"res://.godot/imported/XboxSeriesX_X.png-5edc2dc53019ab9dfe4d3c7218b31e3f.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Y.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bx76tv1exmv0p\"\npath=\"res://.godot/imported/XboxSeriesX_Y.png-6e846c053bd4c131b43c4876c8115048.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Y.png\"\ndest_files=[\"res://.godot/imported/XboxSeriesX_Y.png-6e846c053bd4c131b43c4876c8115048.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/microsoft/microsoft.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEControllerRenderStyle\" load_steps=23 format=3 uid=\"uid://depqolaaisogn\"]\n\n[ext_resource type=\"Texture2D\" uid=\"uid://cujvw3m7aghgh\" path=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_A.png\" id=\"1_j3sh4\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://brgqhp87g40l5\" path=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_B.png\" id=\"2_g54nh\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://b5wc83dex8igr\" path=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_View.png\" id=\"3_djc6n\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://d1fgf8ia7q4vo\" path=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Dpad_Down.png\" id=\"4_g3fuy\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bqtskbk6l8v7w\" path=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Dpad_Left.png\" id=\"5_aaeb2\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://gsilbr1bntic\" path=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Dpad_Right.png\" id=\"6_yoojc\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://boc4an5lj12lp\" path=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Dpad_Up.png\" id=\"7_j7lvn\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bmgxqbypegjxh\" path=\"res://addons/guide/ui/renderers/textures/arrow_horizontal.svg\" id=\"8_oblur\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://uwqbgrkqv7qc\" path=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_LB.png\" id=\"9_veke3\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://wol4p4f32lfr\" path=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Left_Stick.png\" id=\"10_dntol\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://c1p7p5qpqxqfn\" path=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Right_Stick.png\" id=\"11_r8ctv\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://c1rw1hnlt3dlt\" path=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_LT.png\" id=\"12_q3pce\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://b8b2oomlnf5tt\" path=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Share.png\" id=\"13_gst5w\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://5j0mm7ydxc5b\" path=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_RB.png\" id=\"14_4vaex\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://c0xpc73ovry50\" path=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Left_Stick_Click.png\" id=\"15_2yds1\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://pmymadotp1l0\" path=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Right_Stick_Click.png\" id=\"16_dly88\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://dvsukgwjjn78x\" path=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_RT.png\" id=\"17_w0bus\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/ui/renderers/controllers/guide_controller_render_style.gd\" id=\"18_lysir\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://y70m84f40jkk\" path=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Menu.png\" id=\"19_8xww7\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bu5nlug6uf03w\" path=\"res://addons/guide/ui/renderers/textures/arrow_vertical.svg\" id=\"20_pgbag\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://dqnryo1s6qi45\" path=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_X.png\" id=\"21_ladiq\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bx76tv1exmv0p\" path=\"res://addons/guide/ui/renderers/controllers/styles/microsoft/XboxSeriesX_Y.png\" id=\"22_5qum3\"]\n\n[resource]\nscript = ExtResource(\"18_lysir\")\na_button = ExtResource(\"1_j3sh4\")\nb_button = ExtResource(\"2_g54nh\")\nx_button = ExtResource(\"21_ladiq\")\ny_button = ExtResource(\"22_5qum3\")\nleft_stick = ExtResource(\"10_dntol\")\nleft_stick_click = ExtResource(\"15_2yds1\")\nright_stick = ExtResource(\"11_r8ctv\")\nright_stick_click = ExtResource(\"16_dly88\")\nleft_bumper = ExtResource(\"9_veke3\")\nright_bumper = ExtResource(\"14_4vaex\")\nleft_trigger = ExtResource(\"12_q3pce\")\nright_trigger = ExtResource(\"17_w0bus\")\ndpad_up = ExtResource(\"7_j7lvn\")\ndpad_left = ExtResource(\"5_aaeb2\")\ndpad_right = ExtResource(\"6_yoojc\")\ndpad_down = ExtResource(\"4_g3fuy\")\nstart = ExtResource(\"19_8xww7\")\nmisc1 = ExtResource(\"13_gst5w\")\nback = ExtResource(\"3_djc6n\")\nhorizontal = ExtResource(\"8_oblur\")\nvertical = ExtResource(\"20_pgbag\")\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_A.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cl75ptbm7sfi5\"\npath=\"res://.godot/imported/Switch_A.png-0dd055139fb3bef9813549b5c072b669.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_A.png\"\ndest_files=[\"res://.godot/imported/Switch_A.png-0dd055139fb3bef9813549b5c072b669.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_B.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bptn4jygg3p8q\"\npath=\"res://.godot/imported/Switch_B.png-4c9e619eb6dee9fb48db13c1ff3a1346.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_B.png\"\ndest_files=[\"res://.godot/imported/Switch_B.png-4c9e619eb6dee9fb48db13c1ff3a1346.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Controller_Left.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://b0ha1pv08n3fn\"\npath=\"res://.godot/imported/Switch_Controller_Left.png-3d9593638e7dea53de5039c115a024b2.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Controller_Left.png\"\ndest_files=[\"res://.godot/imported/Switch_Controller_Left.png-3d9593638e7dea53de5039c115a024b2.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Controller_Right.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://qv33ijfxtj1x\"\npath=\"res://.godot/imported/Switch_Controller_Right.png-61a541e1d7c2ea70470dc6c40aa89f1d.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Controller_Right.png\"\ndest_files=[\"res://.godot/imported/Switch_Controller_Right.png-61a541e1d7c2ea70470dc6c40aa89f1d.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Controllers.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://dqfhfnjqwcjpk\"\npath=\"res://.godot/imported/Switch_Controllers.png-b865d0fd29d709759d00099e16a8bdb3.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Controllers.png\"\ndest_files=[\"res://.godot/imported/Switch_Controllers.png-b865d0fd29d709759d00099e16a8bdb3.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Controllers_Separate.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://8lsr71y25n8q\"\npath=\"res://.godot/imported/Switch_Controllers_Separate.png-df49c1ad783387f0ce7c31857fadc10c.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Controllers_Separate.png\"\ndest_files=[\"res://.godot/imported/Switch_Controllers_Separate.png-df49c1ad783387f0ce7c31857fadc10c.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Down.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://qt8r1byskjmu\"\npath=\"res://.godot/imported/Switch_Down.png-ebb83179202ee5206df4ec9d34a921f9.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Down.png\"\ndest_files=[\"res://.godot/imported/Switch_Down.png-ebb83179202ee5206df4ec9d34a921f9.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Dpad.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bxvp4elmagomg\"\npath=\"res://.godot/imported/Switch_Dpad.png-16c3780962113d93b736e6b1a84d657e.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Dpad.png\"\ndest_files=[\"res://.godot/imported/Switch_Dpad.png-16c3780962113d93b736e6b1a84d657e.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Dpad_Down.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://dq2ypo4cx3ucs\"\npath=\"res://.godot/imported/Switch_Dpad_Down.png-8d181fec51854368c763ef215494bfe7.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Dpad_Down.png\"\ndest_files=[\"res://.godot/imported/Switch_Dpad_Down.png-8d181fec51854368c763ef215494bfe7.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Dpad_Left.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://rcrsxqeu6mns\"\npath=\"res://.godot/imported/Switch_Dpad_Left.png-2551f6ae7c00da52f30a52033bda1a89.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Dpad_Left.png\"\ndest_files=[\"res://.godot/imported/Switch_Dpad_Left.png-2551f6ae7c00da52f30a52033bda1a89.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Dpad_Right.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://dubah62ttpnc0\"\npath=\"res://.godot/imported/Switch_Dpad_Right.png-9eec040ca865360f4600fc316872ddbd.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Dpad_Right.png\"\ndest_files=[\"res://.godot/imported/Switch_Dpad_Right.png-9eec040ca865360f4600fc316872ddbd.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Dpad_Up.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://u6uclokrrbaq\"\npath=\"res://.godot/imported/Switch_Dpad_Up.png-cdc7ac840f2a8fae13f35e15a565f73d.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Dpad_Up.png\"\ndest_files=[\"res://.godot/imported/Switch_Dpad_Up.png-cdc7ac840f2a8fae13f35e15a565f73d.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Home.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://uf6oq3wbq11f\"\npath=\"res://.godot/imported/Switch_Home.png-d9794095d3a36a99da26eb49896a5d46.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Home.png\"\ndest_files=[\"res://.godot/imported/Switch_Home.png-d9794095d3a36a99da26eb49896a5d46.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_LB.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cb6gvej03avm3\"\npath=\"res://.godot/imported/Switch_LB.png-7d29b113dc8a74b0013071e6c82cd472.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_LB.png\"\ndest_files=[\"res://.godot/imported/Switch_LB.png-7d29b113dc8a74b0013071e6c82cd472.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_LT.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://savy2mhybmun\"\npath=\"res://.godot/imported/Switch_LT.png-ea7ee14b3676f4bd6435a7a8a2701873.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_LT.png\"\ndest_files=[\"res://.godot/imported/Switch_LT.png-ea7ee14b3676f4bd6435a7a8a2701873.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Left.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cyjwul8mif4s2\"\npath=\"res://.godot/imported/Switch_Left.png-0c39a141749278763ff6ec193d5b87c3.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Left.png\"\ndest_files=[\"res://.godot/imported/Switch_Left.png-0c39a141749278763ff6ec193d5b87c3.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Left_Stick.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cha2jimmsyrsg\"\npath=\"res://.godot/imported/Switch_Left_Stick.png-65522a7b278378a630e92575a0dc3080.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Left_Stick.png\"\ndest_files=[\"res://.godot/imported/Switch_Left_Stick.png-65522a7b278378a630e92575a0dc3080.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Left_Stick_Click.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://by1vmleujtq1i\"\npath=\"res://.godot/imported/Switch_Left_Stick_Click.png-4ca5da8deef89c3030e741c5b9a7d45d.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Left_Stick_Click.png\"\ndest_files=[\"res://.godot/imported/Switch_Left_Stick_Click.png-4ca5da8deef89c3030e741c5b9a7d45d.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Minus.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bdydqv6vi48ix\"\npath=\"res://.godot/imported/Switch_Minus.png-1a9a7ddd9a8f07fa00206f2bdd6ffda3.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Minus.png\"\ndest_files=[\"res://.godot/imported/Switch_Minus.png-1a9a7ddd9a8f07fa00206f2bdd6ffda3.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Plus.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://j3wpcxyxsy2r\"\npath=\"res://.godot/imported/Switch_Plus.png-5fc3055261bdf72f5cc5e13928e69f27.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Plus.png\"\ndest_files=[\"res://.godot/imported/Switch_Plus.png-5fc3055261bdf72f5cc5e13928e69f27.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_RB.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://s2xm61tj0mfy\"\npath=\"res://.godot/imported/Switch_RB.png-ca6f57e03314950bfbe43275ad6a4620.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_RB.png\"\ndest_files=[\"res://.godot/imported/Switch_RB.png-ca6f57e03314950bfbe43275ad6a4620.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_RT.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cccvjq78xw3n4\"\npath=\"res://.godot/imported/Switch_RT.png-2ab00786029a8b2d6cfcc6205cb5c359.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_RT.png\"\ndest_files=[\"res://.godot/imported/Switch_RT.png-2ab00786029a8b2d6cfcc6205cb5c359.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Right.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://b3os8st4cai36\"\npath=\"res://.godot/imported/Switch_Right.png-50ff07b1e0bb4a92534ac7e1aaeca5f7.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Right.png\"\ndest_files=[\"res://.godot/imported/Switch_Right.png-50ff07b1e0bb4a92534ac7e1aaeca5f7.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Right_Stick.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://d1jqxuup5llkb\"\npath=\"res://.godot/imported/Switch_Right_Stick.png-ebefcd48783a30f7d575bda863751814.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Right_Stick.png\"\ndest_files=[\"res://.godot/imported/Switch_Right_Stick.png-ebefcd48783a30f7d575bda863751814.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Right_Stick_Click.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://denpxaxemjpg3\"\npath=\"res://.godot/imported/Switch_Right_Stick_Click.png-107c4804164ee16320047ca83d314cfa.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Right_Stick_Click.png\"\ndest_files=[\"res://.godot/imported/Switch_Right_Stick_Click.png-107c4804164ee16320047ca83d314cfa.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Square.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cl0owijnhu5pc\"\npath=\"res://.godot/imported/Switch_Square.png-2baaeb3f25f64f6c859bd065ad0fccd5.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Square.png\"\ndest_files=[\"res://.godot/imported/Switch_Square.png-2baaeb3f25f64f6c859bd065ad0fccd5.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Up.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://dxmy0dvee663b\"\npath=\"res://.godot/imported/Switch_Up.png-d4021d06cb44937140a81bf54b0ac183.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Up.png\"\ndest_files=[\"res://.godot/imported/Switch_Up.png-d4021d06cb44937140a81bf54b0ac183.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_X.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://7c6ie8ef23ox\"\npath=\"res://.godot/imported/Switch_X.png-a5ad46d7c482ceab1cdaa5f7a04f1e2e.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_X.png\"\ndest_files=[\"res://.godot/imported/Switch_X.png-a5ad46d7c482ceab1cdaa5f7a04f1e2e.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Y.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bru7lpc778npo\"\npath=\"res://.godot/imported/Switch_Y.png-346f0c6f5c1510aefbe6427fbcd5d835.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Y.png\"\ndest_files=[\"res://.godot/imported/Switch_Y.png-346f0c6f5c1510aefbe6427fbcd5d835.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/nintendo/nintendo.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEControllerRenderStyle\" load_steps=23 format=3 uid=\"uid://b1ck0lpo6mcpv\"]\n\n[ext_resource type=\"Texture2D\" uid=\"uid://bptn4jygg3p8q\" path=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_B.png\" id=\"1_bvwg4\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://cl75ptbm7sfi5\" path=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_A.png\" id=\"2_p375e\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bdydqv6vi48ix\" path=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Minus.png\" id=\"3_rqqbg\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://dq2ypo4cx3ucs\" path=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Dpad_Down.png\" id=\"4_u4xyb\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://rcrsxqeu6mns\" path=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Dpad_Left.png\" id=\"5_c4qhk\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://dubah62ttpnc0\" path=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Dpad_Right.png\" id=\"6_v8nyh\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://u6uclokrrbaq\" path=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Dpad_Up.png\" id=\"7_wiq8f\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bmgxqbypegjxh\" path=\"res://addons/guide/ui/renderers/textures/arrow_horizontal.svg\" id=\"8_wgyxl\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://cb6gvej03avm3\" path=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_LB.png\" id=\"9_c4x2m\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://cha2jimmsyrsg\" path=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Left_Stick.png\" id=\"10_p4mhj\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://by1vmleujtq1i\" path=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Left_Stick_Click.png\" id=\"11_bw3qq\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://savy2mhybmun\" path=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_LT.png\" id=\"12_8bb0l\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://cl0owijnhu5pc\" path=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Square.png\" id=\"13_bbdo3\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://s2xm61tj0mfy\" path=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_RB.png\" id=\"14_5imgv\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://d1jqxuup5llkb\" path=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Right_Stick.png\" id=\"15_u8qxc\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://denpxaxemjpg3\" path=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Right_Stick_Click.png\" id=\"16_hbspj\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://cccvjq78xw3n4\" path=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_RT.png\" id=\"17_nqree\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/ui/renderers/controllers/guide_controller_render_style.gd\" id=\"18_lkwjo\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://j3wpcxyxsy2r\" path=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Plus.png\" id=\"19_htb2d\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bu5nlug6uf03w\" path=\"res://addons/guide/ui/renderers/textures/arrow_vertical.svg\" id=\"20_b7gr6\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bru7lpc778npo\" path=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_Y.png\" id=\"21_kooww\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://7c6ie8ef23ox\" path=\"res://addons/guide/ui/renderers/controllers/styles/nintendo/Switch_X.png\" id=\"22_6w4y7\"]\n\n[resource]\nscript = ExtResource(\"18_lkwjo\")\na_button = ExtResource(\"1_bvwg4\")\nb_button = ExtResource(\"2_p375e\")\nx_button = ExtResource(\"21_kooww\")\ny_button = ExtResource(\"22_6w4y7\")\nleft_stick = ExtResource(\"10_p4mhj\")\nleft_stick_click = ExtResource(\"11_bw3qq\")\nright_stick = ExtResource(\"15_u8qxc\")\nright_stick_click = ExtResource(\"16_hbspj\")\nleft_bumper = ExtResource(\"9_c4x2m\")\nright_bumper = ExtResource(\"14_5imgv\")\nleft_trigger = ExtResource(\"12_8bb0l\")\nright_trigger = ExtResource(\"17_nqree\")\ndpad_up = ExtResource(\"7_wiq8f\")\ndpad_left = ExtResource(\"5_c4qhk\")\ndpad_right = ExtResource(\"6_v8nyh\")\ndpad_down = ExtResource(\"4_u4xyb\")\nstart = ExtResource(\"19_htb2d\")\nmisc1 = ExtResource(\"13_bbdo3\")\nback = ExtResource(\"3_rqqbg\")\nhorizontal = ExtResource(\"8_wgyxl\")\nvertical = ExtResource(\"20_b7gr6\")\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/sony/PS5_Circle.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://civpcnwgbu5ky\"\npath=\"res://.godot/imported/PS5_Circle.png-79ea0313e13aa2984d4884d790829ae5.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Circle.png\"\ndest_files=[\"res://.godot/imported/PS5_Circle.png-79ea0313e13aa2984d4884d790829ae5.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/sony/PS5_Cross.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cfy1rx4d4wjdh\"\npath=\"res://.godot/imported/PS5_Cross.png-fe7fc62774d406d6a6d331bf8b312d7d.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Cross.png\"\ndest_files=[\"res://.godot/imported/PS5_Cross.png-fe7fc62774d406d6a6d331bf8b312d7d.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/sony/PS5_Dpad.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://ubnurptd6ee2\"\npath=\"res://.godot/imported/PS5_Dpad.png-fed650592cfebda7a5800b5d31a85e80.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Dpad.png\"\ndest_files=[\"res://.godot/imported/PS5_Dpad.png-fed650592cfebda7a5800b5d31a85e80.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/sony/PS5_Dpad_Down.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://vk1vje3280tk\"\npath=\"res://.godot/imported/PS5_Dpad_Down.png-a06b572301dac0fb7604103f64ae2add.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Dpad_Down.png\"\ndest_files=[\"res://.godot/imported/PS5_Dpad_Down.png-a06b572301dac0fb7604103f64ae2add.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/sony/PS5_Dpad_Left.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bkpw61ctv0fbg\"\npath=\"res://.godot/imported/PS5_Dpad_Left.png-243737a27b478bec78211ffefb9022ff.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Dpad_Left.png\"\ndest_files=[\"res://.godot/imported/PS5_Dpad_Left.png-243737a27b478bec78211ffefb9022ff.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/sony/PS5_Dpad_Right.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://dybnayy8y7rxe\"\npath=\"res://.godot/imported/PS5_Dpad_Right.png-0d98b01d2f79d89c7e13ff438fc6ed67.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Dpad_Right.png\"\ndest_files=[\"res://.godot/imported/PS5_Dpad_Right.png-0d98b01d2f79d89c7e13ff438fc6ed67.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/sony/PS5_Dpad_Up.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bvbd876sy2430\"\npath=\"res://.godot/imported/PS5_Dpad_Up.png-73c26102ee1c7df92b77e2cd201da125.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Dpad_Up.png\"\ndest_files=[\"res://.godot/imported/PS5_Dpad_Up.png-73c26102ee1c7df92b77e2cd201da125.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/sony/PS5_L1.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cqgpumb0tf5xr\"\npath=\"res://.godot/imported/PS5_L1.png-fadbd5f1df5d3b1622f59419e5c016fa.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_L1.png\"\ndest_files=[\"res://.godot/imported/PS5_L1.png-fadbd5f1df5d3b1622f59419e5c016fa.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/sony/PS5_L2.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bhoi6nfung5ye\"\npath=\"res://.godot/imported/PS5_L2.png-7f543a3385eb330aef9ac472db3112ba.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_L2.png\"\ndest_files=[\"res://.godot/imported/PS5_L2.png-7f543a3385eb330aef9ac472db3112ba.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/sony/PS5_Left_Stick.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://c3qet180o0dn6\"\npath=\"res://.godot/imported/PS5_Left_Stick.png-5a1af5d1a33bf894b6d1483c19748f28.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Left_Stick.png\"\ndest_files=[\"res://.godot/imported/PS5_Left_Stick.png-5a1af5d1a33bf894b6d1483c19748f28.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/sony/PS5_Left_Stick_Click.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://c0b1sdadfcnbk\"\npath=\"res://.godot/imported/PS5_Left_Stick_Click.png-d38297dc03784399f1cc708e75307a61.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Left_Stick_Click.png\"\ndest_files=[\"res://.godot/imported/PS5_Left_Stick_Click.png-d38297dc03784399f1cc708e75307a61.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/sony/PS5_Microphone.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://eljpu2rrb3k4\"\npath=\"res://.godot/imported/PS5_Microphone.png-b1053605efca6fc271476df9b4700175.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Microphone.png\"\ndest_files=[\"res://.godot/imported/PS5_Microphone.png-b1053605efca6fc271476df9b4700175.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/sony/PS5_Options.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bkttgyeuecjw\"\npath=\"res://.godot/imported/PS5_Options.png-5e82aa9a2313282e93885fe079ee3aa5.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Options.png\"\ndest_files=[\"res://.godot/imported/PS5_Options.png-5e82aa9a2313282e93885fe079ee3aa5.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/sony/PS5_Options_Alt.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://byed3fsjbp82u\"\npath=\"res://.godot/imported/PS5_Options_Alt.png-8160e458fe3cba8ce2fb0aff6f2cd6ac.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Options_Alt.png\"\ndest_files=[\"res://.godot/imported/PS5_Options_Alt.png-8160e458fe3cba8ce2fb0aff6f2cd6ac.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/sony/PS5_R1.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://rwgkfm18pk3l\"\npath=\"res://.godot/imported/PS5_R1.png-efbf41b75aea5f6c593cb18208c15b79.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_R1.png\"\ndest_files=[\"res://.godot/imported/PS5_R1.png-efbf41b75aea5f6c593cb18208c15b79.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/sony/PS5_R2.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://u6ba23igjbj5\"\npath=\"res://.godot/imported/PS5_R2.png-da267432415d9e9fb250745a82a04ed4.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_R2.png\"\ndest_files=[\"res://.godot/imported/PS5_R2.png-da267432415d9e9fb250745a82a04ed4.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/sony/PS5_Right_Stick.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bukgaq1m26bw3\"\npath=\"res://.godot/imported/PS5_Right_Stick.png-09ad1ea193aeaded23d6c2a94a6fb01f.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Right_Stick.png\"\ndest_files=[\"res://.godot/imported/PS5_Right_Stick.png-09ad1ea193aeaded23d6c2a94a6fb01f.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/sony/PS5_Right_Stick_Click.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://c4krmros0va1i\"\npath=\"res://.godot/imported/PS5_Right_Stick_Click.png-b248e63f92828724d0c82218f1aece44.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Right_Stick_Click.png\"\ndest_files=[\"res://.godot/imported/PS5_Right_Stick_Click.png-b248e63f92828724d0c82218f1aece44.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/sony/PS5_Share.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bw2h7xxdtp31i\"\npath=\"res://.godot/imported/PS5_Share.png-247ad3b8303f7c9c1a142259f566a479.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Share.png\"\ndest_files=[\"res://.godot/imported/PS5_Share.png-247ad3b8303f7c9c1a142259f566a479.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/sony/PS5_Share_Alt.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bwje5248woygn\"\npath=\"res://.godot/imported/PS5_Share_Alt.png-6543fcf49a1bd6acf1a5ee1fcdda9758.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Share_Alt.png\"\ndest_files=[\"res://.godot/imported/PS5_Share_Alt.png-6543fcf49a1bd6acf1a5ee1fcdda9758.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/sony/PS5_Square.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://dm6vfcwtodame\"\npath=\"res://.godot/imported/PS5_Square.png-b260b34888df7d1c3730d76e79d4ed7e.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Square.png\"\ndest_files=[\"res://.godot/imported/PS5_Square.png-b260b34888df7d1c3730d76e79d4ed7e.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/sony/PS5_Touch_Pad.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bxxkjsl2u83mp\"\npath=\"res://.godot/imported/PS5_Touch_Pad.png-d330dd85fc30fbe488a7ec1171ef7914.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Touch_Pad.png\"\ndest_files=[\"res://.godot/imported/PS5_Touch_Pad.png-d330dd85fc30fbe488a7ec1171ef7914.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/sony/PS5_Triangle.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bjjj12v4g82g4\"\npath=\"res://.godot/imported/PS5_Triangle.png-bea978116a4d309663c9b3b4510613e1.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Triangle.png\"\ndest_files=[\"res://.godot/imported/PS5_Triangle.png-bea978116a4d309663c9b3b4510613e1.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/sony/sony.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEControllerRenderStyle\" load_steps=24 format=3 uid=\"uid://c46mfn2386oyb\"]\n\n[ext_resource type=\"Texture2D\" uid=\"uid://cfy1rx4d4wjdh\" path=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Cross.png\" id=\"1_am5kd\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://civpcnwgbu5ky\" path=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Circle.png\" id=\"2_8a27q\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bw2h7xxdtp31i\" path=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Share.png\" id=\"3_30ngq\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://vk1vje3280tk\" path=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Dpad_Down.png\" id=\"4_mm0o5\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bkpw61ctv0fbg\" path=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Dpad_Left.png\" id=\"5_ygki3\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://dybnayy8y7rxe\" path=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Dpad_Right.png\" id=\"6_icenk\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bvbd876sy2430\" path=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Dpad_Up.png\" id=\"7_egk1w\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bmgxqbypegjxh\" path=\"res://addons/guide/ui/renderers/textures/arrow_horizontal.svg\" id=\"8_iddvo\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://cqgpumb0tf5xr\" path=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_L1.png\" id=\"9_v2ufa\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://c3qet180o0dn6\" path=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Left_Stick.png\" id=\"10_nwwnw\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://c0b1sdadfcnbk\" path=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Left_Stick_Click.png\" id=\"11_ila4q\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bhoi6nfung5ye\" path=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_L2.png\" id=\"12_e0aew\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://eljpu2rrb3k4\" path=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Microphone.png\" id=\"13_kcrif\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://rwgkfm18pk3l\" path=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_R1.png\" id=\"14_uqcpj\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bukgaq1m26bw3\" path=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Right_Stick.png\" id=\"15_ntssf\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://c4krmros0va1i\" path=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Right_Stick_Click.png\" id=\"16_0ojkd\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://u6ba23igjbj5\" path=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_R2.png\" id=\"17_r1pnn\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/ui/renderers/controllers/guide_controller_render_style.gd\" id=\"18_dqomh\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bkttgyeuecjw\" path=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Options.png\" id=\"19_c1heh\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bxxkjsl2u83mp\" path=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Touch_Pad.png\" id=\"20_7oww7\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bu5nlug6uf03w\" path=\"res://addons/guide/ui/renderers/textures/arrow_vertical.svg\" id=\"21_w4qcp\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://dm6vfcwtodame\" path=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Square.png\" id=\"22_a4leh\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bjjj12v4g82g4\" path=\"res://addons/guide/ui/renderers/controllers/styles/sony/PS5_Triangle.png\" id=\"23_w7tnn\"]\n\n[resource]\nscript = ExtResource(\"18_dqomh\")\na_button = ExtResource(\"1_am5kd\")\nb_button = ExtResource(\"2_8a27q\")\nx_button = ExtResource(\"22_a4leh\")\ny_button = ExtResource(\"23_w7tnn\")\nleft_stick = ExtResource(\"10_nwwnw\")\nleft_stick_click = ExtResource(\"11_ila4q\")\nright_stick = ExtResource(\"15_ntssf\")\nright_stick_click = ExtResource(\"16_0ojkd\")\nleft_bumper = ExtResource(\"9_v2ufa\")\nright_bumper = ExtResource(\"14_uqcpj\")\nleft_trigger = ExtResource(\"12_e0aew\")\nright_trigger = ExtResource(\"17_r1pnn\")\ndpad_up = ExtResource(\"7_egk1w\")\ndpad_left = ExtResource(\"5_ygki3\")\ndpad_right = ExtResource(\"6_icenk\")\ndpad_down = ExtResource(\"4_mm0o5\")\nstart = ExtResource(\"19_c1heh\")\nmisc1 = ExtResource(\"13_kcrif\")\nback = ExtResource(\"3_30ngq\")\ntouch_pad = ExtResource(\"20_7oww7\")\nhorizontal = ExtResource(\"8_iddvo\")\nvertical = ExtResource(\"21_w4qcp\")\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_A.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bhiqnmn8ej4wx\"\npath=\"res://.godot/imported/SteamDeck_A.png-eec29b0784f7714d4f45412d460a2fb4.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_A.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_A.png-eec29b0784f7714d4f45412d460a2fb4.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_B.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://dvq7lhucujofy\"\npath=\"res://.godot/imported/SteamDeck_B.png-fe52eca017b6acb9b5b4fd76e8836d90.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_B.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_B.png-fe52eca017b6acb9b5b4fd76e8836d90.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Dots.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://ca6b61f4n4c1g\"\npath=\"res://.godot/imported/SteamDeck_Dots.png-858549a59ed090eca4605376a8d76714.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Dots.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_Dots.png-858549a59ed090eca4605376a8d76714.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Dpad.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://dxhdwsaqa06se\"\npath=\"res://.godot/imported/SteamDeck_Dpad.png-f8dd88d35da544e24613fd6865a94d76.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Dpad.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_Dpad.png-f8dd88d35da544e24613fd6865a94d76.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Dpad_Down.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://d0yow8ihry7bx\"\npath=\"res://.godot/imported/SteamDeck_Dpad_Down.png-8feec68d4c6eeb0b84e195a5d4c602be.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Dpad_Down.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_Dpad_Down.png-8feec68d4c6eeb0b84e195a5d4c602be.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Dpad_Left.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://dod2pb66on57x\"\npath=\"res://.godot/imported/SteamDeck_Dpad_Left.png-c60efdc32f77eeb451fb74619400eb92.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Dpad_Left.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_Dpad_Left.png-c60efdc32f77eeb451fb74619400eb92.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Dpad_Right.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://b1h313dic42js\"\npath=\"res://.godot/imported/SteamDeck_Dpad_Right.png-1f5d0413aba0b69d0ccdc80695b0535e.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Dpad_Right.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_Dpad_Right.png-1f5d0413aba0b69d0ccdc80695b0535e.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Dpad_Up.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://dx5f1nk7pm4yw\"\npath=\"res://.godot/imported/SteamDeck_Dpad_Up.png-d80cb87d1e71ef06446f554530a31085.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Dpad_Up.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_Dpad_Up.png-d80cb87d1e71ef06446f554530a31085.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Gyro.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cejibh1ytxhti\"\npath=\"res://.godot/imported/SteamDeck_Gyro.png-8d31ed53aa6478814ff6192ce830f41e.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Gyro.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_Gyro.png-8d31ed53aa6478814ff6192ce830f41e.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Inventory.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://c4aaxy677ytpk\"\npath=\"res://.godot/imported/SteamDeck_Inventory.png-a283ce52a83ade0cd6b6ec04b67f655b.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Inventory.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_Inventory.png-a283ce52a83ade0cd6b6ec04b67f655b.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_L1.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cfjm7iwkn1y24\"\npath=\"res://.godot/imported/SteamDeck_L1.png-28837d17ce10e2d9b594563917ff68f4.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_L1.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_L1.png-28837d17ce10e2d9b594563917ff68f4.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_L2.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://1t7o6qn6umay\"\npath=\"res://.godot/imported/SteamDeck_L2.png-7e756f48caa7c537ae098bc1f69d65b6.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_L2.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_L2.png-7e756f48caa7c537ae098bc1f69d65b6.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_L4.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://c22rco6tqpo0p\"\npath=\"res://.godot/imported/SteamDeck_L4.png-00ceb194bae68909be64581e7e543bdb.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_L4.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_L4.png-00ceb194bae68909be64581e7e543bdb.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_L5.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bq6o6fvyg14tl\"\npath=\"res://.godot/imported/SteamDeck_L5.png-97af74df8e8725ee07a7ec1b8e919f21.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_L5.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_L5.png-97af74df8e8725ee07a7ec1b8e919f21.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Left_Stick.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cljg32eotgaxv\"\npath=\"res://.godot/imported/SteamDeck_Left_Stick.png-c64f3234ba8eee544652aef0bd30dad7.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Left_Stick.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_Left_Stick.png-c64f3234ba8eee544652aef0bd30dad7.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Left_Stick_Click.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://b8v01dtnuq2pw\"\npath=\"res://.godot/imported/SteamDeck_Left_Stick_Click.png-5222e9607ded6879d64584fb47e9516e.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Left_Stick_Click.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_Left_Stick_Click.png-5222e9607ded6879d64584fb47e9516e.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Left_Track.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cc16u5y0dksft\"\npath=\"res://.godot/imported/SteamDeck_Left_Track.png-69ed5f8c276bb763680c016c327ac494.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Left_Track.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_Left_Track.png-69ed5f8c276bb763680c016c327ac494.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Menu.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://rt7b2a0cb8dm\"\npath=\"res://.godot/imported/SteamDeck_Menu.png-4840da936669d11971931afd38771a8c.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Menu.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_Menu.png-4840da936669d11971931afd38771a8c.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Minus.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cwbcpo7q0h66x\"\npath=\"res://.godot/imported/SteamDeck_Minus.png-1944882accceb245dbae223354b6784c.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Minus.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_Minus.png-1944882accceb245dbae223354b6784c.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Plus.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://8xgqbi4q7xwe\"\npath=\"res://.godot/imported/SteamDeck_Plus.png-fa2ecb3e4a3b374e936d5dff981761f1.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Plus.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_Plus.png-fa2ecb3e4a3b374e936d5dff981761f1.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Power.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bver4hjksq1mn\"\npath=\"res://.godot/imported/SteamDeck_Power.png-a4767d3d32b22c65c754a97b61335b34.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Power.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_Power.png-a4767d3d32b22c65c754a97b61335b34.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_R1.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://ce58fe08vtff7\"\npath=\"res://.godot/imported/SteamDeck_R1.png-7a47f078a7cafa396ca5fc04879e91be.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_R1.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_R1.png-7a47f078a7cafa396ca5fc04879e91be.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_R2.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://lmm3ihruiet\"\npath=\"res://.godot/imported/SteamDeck_R2.png-23b521b4382cf8b5a0d8a1d67a3608d8.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_R2.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_R2.png-23b521b4382cf8b5a0d8a1d67a3608d8.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_R4.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bpy6su7tg6fhm\"\npath=\"res://.godot/imported/SteamDeck_R4.png-1a4dcc82088203fb29f1245d0c76222d.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_R4.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_R4.png-1a4dcc82088203fb29f1245d0c76222d.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_R5.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://dxryshhxpbrc2\"\npath=\"res://.godot/imported/SteamDeck_R5.png-89c5397a2e1d2d3b2769c7fa69c80ec2.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_R5.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_R5.png-89c5397a2e1d2d3b2769c7fa69c80ec2.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Right_Stick.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://436m16aaq3ug\"\npath=\"res://.godot/imported/SteamDeck_Right_Stick.png-4be61c0324bd834fd5250150c9e43566.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Right_Stick.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_Right_Stick.png-4be61c0324bd834fd5250150c9e43566.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Right_Stick_Click.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://c3vnh1xbmm5fb\"\npath=\"res://.godot/imported/SteamDeck_Right_Stick_Click.png-88e872b4d18a25700aa0045cfd00e6b8.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Right_Stick_Click.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_Right_Stick_Click.png-88e872b4d18a25700aa0045cfd00e6b8.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Right_Track.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://4ebjvbq4jkxa\"\npath=\"res://.godot/imported/SteamDeck_Right_Track.png-0e482b33eba43f879452c52948b2b92f.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Right_Track.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_Right_Track.png-0e482b33eba43f879452c52948b2b92f.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Square.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://dn77c2t6an5ko\"\npath=\"res://.godot/imported/SteamDeck_Square.png-1042040724ce515f10d6fa75b8cfc3b0.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Square.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_Square.png-1042040724ce515f10d6fa75b8cfc3b0.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Steam.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://br65gx7uekosx\"\npath=\"res://.godot/imported/SteamDeck_Steam.png-9c56bb763aff8ecb0da0c2fa80963bbe.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Steam.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_Steam.png-9c56bb763aff8ecb0da0c2fa80963bbe.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_X.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://ddrpf453nwg1w\"\npath=\"res://.godot/imported/SteamDeck_X.png-d040fe0eebab1452c277edbc8b8e7a45.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_X.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_X.png-d040fe0eebab1452c277edbc8b8e7a45.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Y.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://dr4ksxygwlm0t\"\npath=\"res://.godot/imported/SteamDeck_Y.png-6b07249e105e81292169e6eb8b232c04.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Y.png\"\ndest_files=[\"res://.godot/imported/SteamDeck_Y.png-6b07249e105e81292169e6eb8b232c04.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/controllers/styles/steam_deck/steam_deck.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEControllerRenderStyle\" load_steps=22 format=3 uid=\"uid://cyancglq2pbho\"]\n\n[ext_resource type=\"Texture2D\" uid=\"uid://bhiqnmn8ej4wx\" path=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_A.png\" id=\"1_45b2v\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://dvq7lhucujofy\" path=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_B.png\" id=\"2_4c8lu\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://c4aaxy677ytpk\" path=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Inventory.png\" id=\"3_6r1bd\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://d0yow8ihry7bx\" path=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Dpad_Down.png\" id=\"4_457wu\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://dod2pb66on57x\" path=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Dpad_Left.png\" id=\"5_1abhr\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://b1h313dic42js\" path=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Dpad_Right.png\" id=\"6_o1iwf\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://dx5f1nk7pm4yw\" path=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Dpad_Up.png\" id=\"7_ikg7b\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bmgxqbypegjxh\" path=\"res://addons/guide/ui/renderers/textures/arrow_horizontal.svg\" id=\"8_sdyvn\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://cfjm7iwkn1y24\" path=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_L1.png\" id=\"9_h0aci\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://cljg32eotgaxv\" path=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Left_Stick.png\" id=\"10_vwjt2\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://b8v01dtnuq2pw\" path=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Left_Stick_Click.png\" id=\"11_fwaue\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://1t7o6qn6umay\" path=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_L2.png\" id=\"12_sh45p\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://ce58fe08vtff7\" path=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_R1.png\" id=\"14_4xbd3\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://436m16aaq3ug\" path=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Right_Stick.png\" id=\"15_882tx\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://c3vnh1xbmm5fb\" path=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Right_Stick_Click.png\" id=\"16_3reu6\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://lmm3ihruiet\" path=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_R2.png\" id=\"17_13ssj\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://rt7b2a0cb8dm\" path=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Menu.png\" id=\"18_hphon\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/ui/renderers/controllers/guide_controller_render_style.gd\" id=\"18_rcbnj\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bu5nlug6uf03w\" path=\"res://addons/guide/ui/renderers/textures/arrow_vertical.svg\" id=\"21_jsw57\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://ddrpf453nwg1w\" path=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_X.png\" id=\"22_amuwn\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://dr4ksxygwlm0t\" path=\"res://addons/guide/ui/renderers/controllers/styles/steam_deck/SteamDeck_Y.png\" id=\"23_4l4dn\"]\n\n[resource]\nscript = ExtResource(\"18_rcbnj\")\na_button = ExtResource(\"1_45b2v\")\nb_button = ExtResource(\"2_4c8lu\")\nx_button = ExtResource(\"22_amuwn\")\ny_button = ExtResource(\"23_4l4dn\")\nleft_stick = ExtResource(\"10_vwjt2\")\nleft_stick_click = ExtResource(\"11_fwaue\")\nright_stick = ExtResource(\"15_882tx\")\nright_stick_click = ExtResource(\"16_3reu6\")\nleft_bumper = ExtResource(\"9_h0aci\")\nright_bumper = ExtResource(\"14_4xbd3\")\nleft_trigger = ExtResource(\"12_sh45p\")\nright_trigger = ExtResource(\"17_13ssj\")\ndpad_up = ExtResource(\"7_ikg7b\")\ndpad_left = ExtResource(\"5_1abhr\")\ndpad_right = ExtResource(\"6_o1iwf\")\ndpad_down = ExtResource(\"4_457wu\")\nstart = ExtResource(\"18_hphon\")\nback = ExtResource(\"3_6r1bd\")\nhorizontal = ExtResource(\"8_sdyvn\")\nvertical = ExtResource(\"21_jsw57\")\n"
  },
  {
    "path": "addons/guide/ui/renderers/joy/guide_joy_render_style.gd",
    "content": "@tool\n@icon(\"../style.svg\")\n## An style used by the joy renderer.\nclass_name GUIDEJoyRenderStyle\nextends Resource\n\n## Texture to use for joy buttons.\n@export var button:Texture2D\n\n## Texture to use for joy sticks.\n@export var stick:Texture2D\n\n## The font to use for the label\n@export var font:Font\n\n## The color to use for the font.\n@export var font_color:Color = Color(0.843, 0.843, 0.843)\n\n## The font size to use in px.\n@export var font_size:int = 50\n\n@export_category(\"Directions\")\n\n## An icon indicating horizontal movement.\n@export var horizontal:Texture2D\n\n## An icon indicating vertical movement.\n@export var vertical:Texture2D\n"
  },
  {
    "path": "addons/guide/ui/renderers/joy/guide_joy_render_style.gd.uid",
    "content": "uid://3putvlp32vmf\n"
  },
  {
    "path": "addons/guide/ui/renderers/joy/guide_joy_renderer.gd",
    "content": "@tool\nclass_name GUIDEJoyRenderer\nextends GUIDEIconRenderer\n\n@onready var _stick:TextureRect = %Stick\n@onready var _button:TextureRect = %Button\n@onready var _text:Label = %Text\n@onready var _directions:Control = %Directions\n@onready var _horizontal:TextureRect = %Horizontal\n@onready var _vertical:TextureRect = %Vertical\n\n\nstatic var _style:GUIDEJoyRenderStyle = preload(\"styles/default.tres\")\n\nstatic func set_style(style:GUIDEJoyRenderStyle) -> void:\n\tif not is_instance_valid(style):\n\t\tpush_error(\"Joy style must not be null.\")\n\t\treturn\n\t_style = style\n\nfunc supports(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> bool:\n\treturn input is GUIDEInputJoyBase\n\t\nfunc render(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> void:\n\t_stick.texture = _style.stick\n\t_stick.visible = false\n\t\n\t_button.texture = _style.button\n\t_button.visible = false\n\t\n\t_directions.visible = false\n\t\n\t_horizontal.texture = _style.horizontal\n\t_horizontal.visible = false\n\t\n\t_vertical.texture = _style.vertical\n\t_vertical.visible = false\t\n\t\n\t_text.text = \"\"\n\t_text.add_theme_color_override(\"font_color\", _style.font_color)\n\t_text.add_theme_font_override(\"font\", _style.font)\n\t_text.add_theme_font_size_override(\"font_size\", _style.font_size)\n\n\t\t\n\tif input is GUIDEInputJoyAxis1D:\n\t\t_stick.visible = true\n\t\tmatch input.axis:\n\t\t\tJOY_AXIS_LEFT_X:\n\t\t\t\t_directions.visible = true\n\t\t\t\t_text.text = \"1\"\n\t\t\t\t_horizontal.visible = true\n\t\t\tJOY_AXIS_RIGHT_X:\n\t\t\t\t_directions.visible = true\n\t\t\t\t_text.text = \"2\"\n\t\t\t\t_horizontal.visible = true\n\t\t\tJOY_AXIS_LEFT_Y:\n\t\t\t\t_directions.visible = true\n\t\t\t\t_text.text = \"1\"\n\t\t\t\t_vertical.visible = true\n\t\t\tJOY_AXIS_RIGHT_Y:\n\t\t\t\t_directions.visible = true\n\t\t\t\t_text.text = \"2\"\n\t\t\t\t_vertical.visible = true\n\t\t\tJOY_AXIS_TRIGGER_LEFT:\n\t\t\t\t_text.text = \"3\"\n\t\t\tJOY_AXIS_TRIGGER_RIGHT:\n\t\t\t\t_text.text = \"4\"\n\t\t\t\t\t\t\t\t\n\t\n\t\n\tif input is GUIDEInputJoyAxis2D:\n\t\t_stick.visible = true\n\t\tmatch input.x:\n\t\t\tJOY_AXIS_LEFT_X, JOY_AXIS_LEFT_Y:\n\t\t\t\t_text.text = \"1\"\n\t\t\tJOY_AXIS_RIGHT_X, JOY_AXIS_RIGHT_Y:\n\t\t\t\t_text.text = \"2\"\n\t\t\t_:\n\t\t\t\t# well we don't know really what this is but what can we do.\n\t\t\t\t_text.text = str(input.x + input.y)\n\t\t\n\tif input is GUIDEInputJoyButton:\n\t\t_button.visible = true\n\t\t_text.text = str(input.button)\n\t\t\t\n\tcall(\"queue_sort\")\n \nfunc cache_key(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> String:\n\t# output depends on the input and the currently used style\n\treturn \"a9ced629-de65-4c31-9de0-8e4cbf88a2e0\" \\\n\t\t + input.to_string() \\\n\t\t + _style.resource_path\n"
  },
  {
    "path": "addons/guide/ui/renderers/joy/guide_joy_renderer.gd.uid",
    "content": "uid://byfsfu4qx7ovs\n"
  },
  {
    "path": "addons/guide/ui/renderers/joy/guide_joy_renderer.tscn",
    "content": "[gd_scene load_steps=7 format=3 uid=\"uid://c6sqf8rur1wss\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/ui/renderers/joy/guide_joy_renderer.gd\" id=\"1_wpc4y\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://do001o6aysaxo\" path=\"res://addons/guide/ui/renderers/joy/styles/stick_empty.png\" id=\"2_uoqkj\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://veqjcwokdukw\" path=\"res://addons/guide/ui/renderers/joy/styles/button_empty.png\" id=\"3_xu0fv\"]\n[ext_resource type=\"FontFile\" uid=\"uid://cu8bvod6tnnwr\" path=\"res://addons/guide/ui/renderers/keyboard/styles/Lato-Black.ttf\" id=\"4_dofui\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bmgxqbypegjxh\" path=\"res://addons/guide/ui/renderers/textures/arrow_horizontal.svg\" id=\"5_jp034\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bu5nlug6uf03w\" path=\"res://addons/guide/ui/renderers/textures/arrow_vertical.svg\" id=\"6_jimqg\"]\n\n[node name=\"JoyRenderer\" type=\"MarginContainer\"]\nprocess_mode = 3\ncustom_minimum_size = Vector2(128, 128)\noffset_right = 100.0\noffset_bottom = 100.0\nsize_flags_horizontal = 0\nscript = ExtResource(\"1_wpc4y\")\n\n[node name=\"Controls\" type=\"MarginContainer\" parent=\".\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntheme_override_constants/margin_left = 14\ntheme_override_constants/margin_top = 14\ntheme_override_constants/margin_right = 14\ntheme_override_constants/margin_bottom = 14\n\n[node name=\"Stick\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntexture = ExtResource(\"2_uoqkj\")\nstretch_mode = 5\n\n[node name=\"Button\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntexture = ExtResource(\"3_xu0fv\")\nstretch_mode = 5\n\n[node name=\"Text\" type=\"Label\" parent=\"Controls\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 4\ntheme_override_colors/font_color = Color(0.843137, 0.843137, 0.843137, 1)\ntheme_override_fonts/font = ExtResource(\"4_dofui\")\ntheme_override_font_sizes/font_size = 50\ntext = \"1\"\nhorizontal_alignment = 1\nvertical_alignment = 1\n\n[node name=\"Directions\" type=\"MarginContainer\" parent=\".\"]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\n\n[node name=\"Horizontal\" type=\"TextureRect\" parent=\"Directions\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntexture = ExtResource(\"5_jp034\")\nstretch_mode = 5\n\n[node name=\"Vertical\" type=\"TextureRect\" parent=\"Directions\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntexture = ExtResource(\"6_jimqg\")\nstretch_mode = 5\n"
  },
  {
    "path": "addons/guide/ui/renderers/joy/styles/button_empty.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://veqjcwokdukw\"\npath=\"res://.godot/imported/button_empty.png-4fb3c781403527a8774b7a306f93f540.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/joy/styles/button_empty.png\"\ndest_files=[\"res://.godot/imported/button_empty.png-4fb3c781403527a8774b7a306f93f540.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/joy/styles/default.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEJoyRenderStyle\" load_steps=7 format=3 uid=\"uid://x36d7y75jfnr\"]\n\n[ext_resource type=\"Texture2D\" uid=\"uid://veqjcwokdukw\" path=\"res://addons/guide/ui/renderers/joy/styles/button_empty.png\" id=\"1_ctnxf\"]\n[ext_resource type=\"FontFile\" uid=\"uid://cu8bvod6tnnwr\" path=\"res://addons/guide/ui/renderers/keyboard/styles/Lato-Black.ttf\" id=\"2_uodat\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://do001o6aysaxo\" path=\"res://addons/guide/ui/renderers/joy/styles/stick_empty.png\" id=\"3_0cij2\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bmgxqbypegjxh\" path=\"res://addons/guide/ui/renderers/textures/arrow_horizontal.svg\" id=\"3_5228y\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/ui/renderers/joy/guide_joy_render_style.gd\" id=\"4_ms3wg\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bu5nlug6uf03w\" path=\"res://addons/guide/ui/renderers/textures/arrow_vertical.svg\" id=\"6_p8c8i\"]\n\n[resource]\nscript = ExtResource(\"4_ms3wg\")\nbutton = ExtResource(\"1_ctnxf\")\nstick = ExtResource(\"3_0cij2\")\nfont = ExtResource(\"2_uodat\")\nfont_color = Color(0.843, 0.843, 0.843, 1)\nfont_size = 50\nhorizontal = ExtResource(\"3_5228y\")\nvertical = ExtResource(\"6_p8c8i\")\n"
  },
  {
    "path": "addons/guide/ui/renderers/joy/styles/stick_empty.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://do001o6aysaxo\"\npath=\"res://.godot/imported/stick_empty.png-d41d4bf5e785ed2ba293837ad21eb480.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/joy/styles/stick_empty.png\"\ndest_files=[\"res://.godot/imported/stick_empty.png-d41d4bf5e785ed2ba293837ad21eb480.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/keyboard/guide_key_render_style.gd",
    "content": "@tool\n@icon(\"../style.svg\")\n## An style used by the key renderer.\nclass_name GUIDEKeyRenderStyle\nextends Resource\n\n## Texture to use for the keycaps.\n@export var keycaps:Texture2D\n\n\n## The region of the texture to use. If this rect has no size, it will be ignored.\n@export var region_rect:Rect2 = Rect2()\n\n@export_group(\"Patch margin\", \"patch_margin\")\n@export_range(0, 100, 1, \"or_greater\") var patch_margin_left:int = 0\n@export_range(0, 100, 1, \"or_greater\") var patch_margin_top:int = 0 \n@export_range(0, 100, 1, \"or_greater\") var patch_margin_right:int = 0\n@export_range(0, 100, 1, \"or_greater\") var patch_margin_bottom:int = 0\n\n## The font to use for the key label.\n@export var font:Font\n\n## The color to use for the font.\n@export var font_color:Color = Color(0.843, 0.843, 0.843)\n\n## The font size to use in px.\n@export var font_size:int = 45\n"
  },
  {
    "path": "addons/guide/ui/renderers/keyboard/guide_key_render_style.gd.uid",
    "content": "uid://kjmhcoww0pte\n"
  },
  {
    "path": "addons/guide/ui/renderers/keyboard/guide_key_renderer.gd",
    "content": "@tool\nclass_name GUIDEKeyRenderer\nextends GUIDEIconRenderer\n\n@onready var _label:Label = %Label\n@onready var _nine_patch_rect: NinePatchRect = %NinePatchRect\n\nstatic var _style:GUIDEKeyRenderStyle = preload(\"styles/default.tres\")\n\n## Sets the style to be used by this renderer.\nstatic func set_style(style:GUIDEKeyRenderStyle) -> void:\n\tif not is_instance_valid(style):\n\t\tpush_error(\"Key style must not be null.\")\n\t\treturn\n\t_style = style\n\nfunc supports(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> bool:\n\treturn input is GUIDEInputKey\n\t\nfunc render(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> void:\n\tvar key:Key = input.key\n\t\n\t_nine_patch_rect.texture = _style.keycaps\n\t_nine_patch_rect.region_rect = _style.region_rect\n\t_nine_patch_rect.patch_margin_left = _style.patch_margin_left\n\t_nine_patch_rect.patch_margin_top = _style.patch_margin_top\n\t_nine_patch_rect.patch_margin_right = _style.patch_margin_right\n\t_nine_patch_rect.patch_margin_bottom = _style.patch_margin_bottom\n\t\n\tvar label_key:Key = DisplayServer.keyboard_get_label_from_physical(key)\n\t_label.text = OS.get_keycode_string(label_key).strip_edges()\n\t_label.add_theme_color_override(\"font_color\", _style.font_color)\n\t_label.add_theme_font_override(\"font\", _style.font)\n\t_label.add_theme_font_size_override(\"font_size\", _style.font_size)\n\tsize = Vector2.ZERO\n\t\n\t\n\tcall(\"queue_sort\")\n \nfunc cache_key(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> String:\n\t# Output depends on the input and the style\n\treturn \"ed6923d5-4115-44bd-b35e-2c4102ffc83e\" \\\n\t\t+ input.to_string() \\\n\t\t+ _style.resource_path\n"
  },
  {
    "path": "addons/guide/ui/renderers/keyboard/guide_key_renderer.gd.uid",
    "content": "uid://exayet6s5prr5\n"
  },
  {
    "path": "addons/guide/ui/renderers/keyboard/guide_key_renderer.tscn",
    "content": "[gd_scene load_steps=4 format=3 uid=\"uid://toty2e3yx26l\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/ui/renderers/keyboard/guide_key_renderer.gd\" id=\"1_i8mmb\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://b3x586os8uuwb\" path=\"res://addons/guide/ui/renderers/keyboard/styles/Blank_White_Normal.png\" id=\"2_sckay\"]\n[ext_resource type=\"FontFile\" uid=\"uid://cu8bvod6tnnwr\" path=\"res://addons/guide/ui/renderers/keyboard/styles/Lato-Black.ttf\" id=\"3_6l21u\"]\n\n[node name=\"KeyRenderer\" type=\"MarginContainer\"]\ncustom_minimum_size = Vector2(100, 100)\noffset_right = 267.0\noffset_bottom = 100.0\nsize_flags_horizontal = 0\nsize_flags_vertical = 0\nscript = ExtResource(\"1_i8mmb\")\n\n[node name=\"NinePatchRect\" type=\"NinePatchRect\" parent=\".\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntexture = ExtResource(\"2_sckay\")\nregion_rect = Rect2(10, 10, 80, 80)\npatch_margin_left = 29\npatch_margin_top = 30\npatch_margin_right = 29\npatch_margin_bottom = 29\n\n[node name=\"MarginContainer\" type=\"MarginContainer\" parent=\".\"]\nlayout_mode = 2\ntheme_override_constants/margin_left = 30\ntheme_override_constants/margin_right = 30\n\n[node name=\"Label\" type=\"Label\" parent=\"MarginContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntheme_override_colors/font_color = Color(0.25098, 0.25098, 0.25098, 1)\ntheme_override_fonts/font = ExtResource(\"3_6l21u\")\ntheme_override_font_sizes/font_size = 45\ntext = \"Long Long Long\"\nhorizontal_alignment = 1\n"
  },
  {
    "path": "addons/guide/ui/renderers/keyboard/styles/Blank_White_Normal.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://b3x586os8uuwb\"\npath=\"res://.godot/imported/Blank_White_Normal.png-f7aa6213ac2ca6009b718f601cd7ca89.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/keyboard/styles/Blank_White_Normal.png\"\ndest_files=[\"res://.godot/imported/Blank_White_Normal.png-f7aa6213ac2ca6009b718f601cd7ca89.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/keyboard/styles/Lato-Black.ttf.import",
    "content": "[remap]\n\nimporter=\"font_data_dynamic\"\ntype=\"FontFile\"\nuid=\"uid://cu8bvod6tnnwr\"\npath=\"res://.godot/imported/Lato-Black.ttf-6547c8c29820ffd3a30997a9724de39d.fontdata\"\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/keyboard/styles/Lato-Black.ttf\"\ndest_files=[\"res://.godot/imported/Lato-Black.ttf-6547c8c29820ffd3a30997a9724de39d.fontdata\"]\n\n[params]\n\nRendering=null\nantialiasing=1\ngenerate_mipmaps=false\nmultichannel_signed_distance_field=false\nmsdf_pixel_range=8\nmsdf_size=48\nallow_system_fallback=true\nforce_autohinter=false\nhinting=1\nsubpixel_positioning=1\noversampling=0.0\nFallbacks=null\nfallbacks=[]\nCompress=null\ncompress=true\npreload=[]\nlanguage_support={}\nscript_support={}\nopentype_features={}\n"
  },
  {
    "path": "addons/guide/ui/renderers/keyboard/styles/default.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEKeyRenderStyle\" load_steps=4 format=3 uid=\"uid://ctk0epydr07en\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/ui/renderers/keyboard/guide_key_render_style.gd\" id=\"1_8d7yy\"]\n[ext_resource type=\"FontFile\" uid=\"uid://cu8bvod6tnnwr\" path=\"res://addons/guide/ui/renderers/keyboard/styles/Lato-Black.ttf\" id=\"1_qtcq6\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://b3x586os8uuwb\" path=\"res://addons/guide/ui/renderers/keyboard/styles/Blank_White_Normal.png\" id=\"2_3oqp6\"]\n\n[resource]\nscript = ExtResource(\"1_8d7yy\")\nkeycaps = ExtResource(\"2_3oqp6\")\nregion_rect = Rect2(10, 10, 80, 80)\npatch_margin_left = 29\npatch_margin_top = 30\npatch_margin_right = 29\npatch_margin_bottom = 29\nfont = ExtResource(\"1_qtcq6\")\nfont_color = Color(0.25098, 0.25098, 0.25098, 1)\nfont_size = 45\n"
  },
  {
    "path": "addons/guide/ui/renderers/misc/guide_action_renderer.gd",
    "content": "@tool\nextends GUIDEIconRenderer\n\nfunc supports(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> bool:\n\treturn input is GUIDEInputAction\n\t\nfunc render(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> void:\n\tpass\n \nfunc cache_key(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> String:\n\treturn \"0ecd6608-ba3c-4fc2-83f7-ad61736f1106\"  # we only have one output, so same cache key\n"
  },
  {
    "path": "addons/guide/ui/renderers/misc/guide_action_renderer.gd.uid",
    "content": "uid://e4kfqtrs4je5e\n"
  },
  {
    "path": "addons/guide/ui/renderers/misc/guide_action_renderer.tscn",
    "content": "[gd_scene load_steps=3 format=3 uid=\"uid://ortn6jb3wljf\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/ui/renderers/misc/guide_action_renderer.gd\" id=\"1_jv0am\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://diwkvjkss2ie\" path=\"res://addons/guide/ui/renderers/textures/action.svg\" id=\"2_gahr5\"]\n\n[node name=\"ActionRenderer\" type=\"MarginContainer\"]\nprocess_mode = 3\noffset_right = 512.0\noffset_bottom = 512.0\nsize_flags_horizontal = 0\nscript = ExtResource(\"1_jv0am\")\n\n[node name=\"Action\" type=\"TextureRect\" parent=\".\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntexture = ExtResource(\"2_gahr5\")\nstretch_mode = 4\n"
  },
  {
    "path": "addons/guide/ui/renderers/misc/guide_fallback_renderer.gd",
    "content": "@tool\nextends GUIDEIconRenderer\n\nfunc _init() -> void:\n\tpriority = 100\n\nfunc supports(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> bool:\n\treturn true\n\t\nfunc render(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> void:\n\tpass\n \nfunc cache_key(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> String:\n\treturn \"2e130e8b-d5b3-478c-af65-53415adfd6bb\"  # we only have one output, so same cache key\n"
  },
  {
    "path": "addons/guide/ui/renderers/misc/guide_fallback_renderer.gd.uid",
    "content": "uid://bswfl5fqxe6jl\n"
  },
  {
    "path": "addons/guide/ui/renderers/misc/guide_fallback_renderer.tscn",
    "content": "[gd_scene load_steps=3 format=3 uid=\"uid://bqf4yoind3a82\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/ui/renderers/misc/guide_fallback_renderer.gd\" id=\"1_e80t5\"]\n[ext_resource type=\"FontFile\" uid=\"uid://cu8bvod6tnnwr\" path=\"res://addons/guide/ui/renderers/keyboard/styles/Lato-Black.ttf\" id=\"2_omgk1\"]\n\n[node name=\"FallbackRenderer\" type=\"MarginContainer\"]\nprocess_mode = 3\noffset_right = 512.0\noffset_bottom = 512.0\nsize_flags_horizontal = 0\nscript = ExtResource(\"1_e80t5\")\npriority = 100\n\n[node name=\"Label\" type=\"Label\" parent=\".\"]\ncustom_minimum_size = Vector2(512, 512)\nlayout_mode = 2\ntheme_override_fonts/font = ExtResource(\"2_omgk1\")\ntheme_override_font_sizes/font_size = 350\ntext = \"?\"\nhorizontal_alignment = 1\nvertical_alignment = 1\n"
  },
  {
    "path": "addons/guide/ui/renderers/mouse/guide_mouse_render_style.gd",
    "content": "@tool\n@icon(\"../style.svg\")\n## A render style for the mouse renderer.\nclass_name GUIDEMouseRenderStyle\nextends Resource\n\n## The mouse with no keys pressed.\n@export var mouse_blank:Texture2D\n\n## The mouse with the left key pressed.\n@export var mouse_left:Texture2D\n\n## The mouse with the right key pressed.\n@export var mouse_right:Texture2D\n\n## The mouse with the middle key pressed.\n@export var mouse_middle:Texture2D\n\n## The mouse with the A side key pressed.\n@export var mouse_side_a:Texture2D\n\n## The mouse with the B side key pressed.\n@export var mouse_side_b:Texture2D\n\n## The mouse cursor.\n@export var mouse_cursor:Texture2D\n\n\n@export_category(\"Directions\")\n\n## An icon indicating movement to the left.\n@export var left:Texture2D\n\n## An icon indicating movement to the right.\n@export var right:Texture2D\n\n## An icon indicating movement up.\n@export var up:Texture2D\n\n## An icon indicating movement down.\n@export var down:Texture2D\n\n## An icon indicating horizontal movement.\n@export var horizontal:Texture2D\n\n## An icon indicating vertical movement.\n@export var vertical:Texture2D\n"
  },
  {
    "path": "addons/guide/ui/renderers/mouse/guide_mouse_render_style.gd.uid",
    "content": "uid://bsl3fwebcwgcu\n"
  },
  {
    "path": "addons/guide/ui/renderers/mouse/guide_mouse_renderer.gd",
    "content": "@tool\nclass_name GUIDEMouseRenderer\nextends GUIDEIconRenderer\n\n@onready var _controls:Control = %Controls\n@onready var _mouse_left:TextureRect = %MouseLeft\n@onready var _mouse_right:TextureRect = %MouseRight\n@onready var _mouse_middle:TextureRect = %MouseMiddle\n@onready var _mouse_side_a:TextureRect = %MouseSideA\n@onready var _mouse_side_b:TextureRect = %MouseSideB\n@onready var _mouse_blank:TextureRect = %MouseBlank\n@onready var _mouse_cursor:TextureRect = %MouseCursor\n\n\n@onready var _directions:Control = %Directions\n@onready var _left:TextureRect = %Left\n@onready var _right:TextureRect = %Right\n@onready var _up:TextureRect = %Up\n@onready var _down:TextureRect = %Down\n@onready var _horizontal:TextureRect = %Horizontal\n@onready var _vertical:TextureRect = %Vertical\n\nstatic var _style:GUIDEMouseRenderStyle = preload(\"styles/default.tres\")\n\nstatic func set_style(style:GUIDEMouseRenderStyle) -> void:\n\tif not is_instance_valid(style):\n\t\tpush_error(\"Mouse style must not be null.\")\n\t\treturn\n\t_style = style\n\t\n\n\nfunc supports(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> bool:\n\treturn input is GUIDEInputMouseButton or \\\n\t\tinput is GUIDEInputMouseAxis1D or \\\n\t\tinput is GUIDEInputMouseAxis2D or \\\n\t\tinput is GUIDEInputMousePosition\n\t\n\t\nfunc render(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> void:\n\tfor child in _controls.get_children():\n\t\tchild.visible = false\n\tfor child in _directions.get_children():\n\t\tchild.visible = false\n\t\t\n\t_directions.visible = false\n\t\n\t_mouse_blank.texture = _style.mouse_blank\n\t_mouse_left.texture = _style.mouse_left\n\t_mouse_right.texture = _style.mouse_right\n\t_mouse_middle.texture = _style.mouse_middle\n\t_mouse_side_a.texture = _style.mouse_side_a\n\t_mouse_side_b.texture = _style.mouse_side_b\n\t_mouse_cursor.texture = _style.mouse_cursor\n\t\n\t_left.texture = _style.left\n\t_right.texture = _style.right\n\t_up.texture = _style.up\n\t_down.texture = _style.down\n\t_horizontal.texture = _style.horizontal\n\t_vertical.texture = _style.vertical\t\n\t\t\n\tif input is GUIDEInputMouseButton:\n\t\tmatch input.button:\n\t\t\tMOUSE_BUTTON_LEFT:\n\t\t\t\t_mouse_left.visible = true\n\t\t\tMOUSE_BUTTON_RIGHT:\n\t\t\t\t_mouse_right.visible = true\n\t\t\tMOUSE_BUTTON_MIDDLE:\n\t\t\t\t_mouse_middle.visible = true\n\t\t\tMOUSE_BUTTON_WHEEL_UP:\n\t\t\t\t_directions.visible = true\n\t\t\t\t_up.visible = true\n\t\t\t\t_mouse_middle.visible = true\n\t\t\tMOUSE_BUTTON_WHEEL_DOWN:\n\t\t\t\t_directions.visible = true\n\t\t\t\t_down.visible = true\n\t\t\t\t_mouse_middle.visible = true\n\t\t\tMOUSE_BUTTON_WHEEL_LEFT:\n\t\t\t\t_directions.visible = true\n\t\t\t\t_left.visible = true\n\t\t\t\t_mouse_middle.visible = true\n\t\t\tMOUSE_BUTTON_WHEEL_RIGHT:\n\t\t\t\t_directions.visible = true\n\t\t\t\t_right.visible = true\n\t\t\t\t_mouse_middle.visible = true\n\t\t\tMOUSE_BUTTON_XBUTTON1:\n\t\t\t\t_mouse_side_a.visible = true\n\t\t\tMOUSE_BUTTON_XBUTTON2:\n\t\t\t\t_mouse_side_b.visible = true\n\t\t\t\t\n\tif input is GUIDEInputMouseAxis1D:\n\t\tif input.axis == GUIDEInputMouseAxis1D.GUIDEInputMouseAxis.X:\n\t\t\t_mouse_blank.visible = true\n\t\t\t_directions.visible = true\n\t\t\t_horizontal.visible = true\n\t\telse:\n\t\t\t_mouse_blank.visible = true\n\t\t\t_directions.visible = true\n\t\t\t_vertical.visible = true\t\t\n\t\t\n\tif input is GUIDEInputMouseAxis2D:\n\t\t_mouse_blank.visible = true\n\t\t\n\tif input is GUIDEInputMousePosition:\n\t\t_mouse_cursor.visible = true\n\t\n\tcall(\"queue_sort\")\n \nfunc cache_key(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> String:\n\t# output depends on input and style\n\treturn \"7e27520a-b6d8-4451-858d-e94330c82e85\" \\\n\t\t+ input.to_string() \\\n\t\t+ _style.resource_path\n"
  },
  {
    "path": "addons/guide/ui/renderers/mouse/guide_mouse_renderer.gd.uid",
    "content": "uid://pv0kgpo04alh\n"
  },
  {
    "path": "addons/guide/ui/renderers/mouse/guide_mouse_renderer.tscn",
    "content": "[gd_scene load_steps=15 format=3 uid=\"uid://bfl6dbw21xqs1\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/ui/renderers/mouse/guide_mouse_renderer.gd\" id=\"1_4r710\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://b3uxk5agbpmab\" path=\"res://addons/guide/ui/renderers/mouse/styles/Mouse_Simple_Key_Light.png\" id=\"2_t8pey\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://vvgpheda22ew\" path=\"res://addons/guide/ui/renderers/mouse/styles/Mouse_Left_Key_Light.png\" id=\"3_x0waw\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://b8bsyguf4qw6f\" path=\"res://addons/guide/ui/renderers/mouse/styles/Mouse_Right_Key_Light.png\" id=\"4_dtdsa\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bmj244x0jn7v2\" path=\"res://addons/guide/ui/renderers/mouse/styles/Mouse_Middle_Key_Light.png\" id=\"5_kw2v6\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://je8rm7jk2nxd\" path=\"res://addons/guide/ui/renderers/mouse/styles/Mouse_Side_Key_1_Light.png\" id=\"6_mbavq\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bqxly0g8pftxa\" path=\"res://addons/guide/ui/renderers/mouse/styles/Mouse_Side_Key_2_Light.png\" id=\"7_eseen\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://ci7icm3q4l1sg\" path=\"res://addons/guide/ui/renderers/mouse/styles/Mouse_Cursor.png\" id=\"8_gauyw\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://1swh072gtbb4\" path=\"res://addons/guide/ui/renderers/textures/arrow_left.svg\" id=\"9_xglas\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://cjvs04qsrj8ap\" path=\"res://addons/guide/ui/renderers/textures/arrow_right.svg\" id=\"10_8jstk\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://ni6lsbx1d2hf\" path=\"res://addons/guide/ui/renderers/textures/arrow_up.svg\" id=\"11_edlod\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://oq2vvwgbdsh7\" path=\"res://addons/guide/ui/renderers/textures/arrow_down.svg\" id=\"12_f7esb\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bmgxqbypegjxh\" path=\"res://addons/guide/ui/renderers/textures/arrow_horizontal.svg\" id=\"13_8atfk\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bu5nlug6uf03w\" path=\"res://addons/guide/ui/renderers/textures/arrow_vertical.svg\" id=\"14_5j0sm\"]\n\n[node name=\"MouseRenderer\" type=\"MarginContainer\"]\nprocess_mode = 3\ncustom_minimum_size = Vector2(128, 128)\noffset_right = 100.0\noffset_bottom = 100.0\nsize_flags_horizontal = 0\nscript = ExtResource(\"1_4r710\")\n\n[node name=\"Controls\" type=\"MarginContainer\" parent=\".\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntheme_override_constants/margin_left = 14\ntheme_override_constants/margin_top = 14\ntheme_override_constants/margin_right = 14\ntheme_override_constants/margin_bottom = 14\n\n[node name=\"MouseBlank\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\ntexture = ExtResource(\"2_t8pey\")\nstretch_mode = 5\n\n[node name=\"MouseLeft\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\ntexture = ExtResource(\"3_x0waw\")\nstretch_mode = 5\n\n[node name=\"MouseRight\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\ntexture = ExtResource(\"4_dtdsa\")\nstretch_mode = 5\n\n[node name=\"MouseMiddle\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\ntexture = ExtResource(\"5_kw2v6\")\nstretch_mode = 5\n\n[node name=\"MouseSideA\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\ntexture = ExtResource(\"6_mbavq\")\nstretch_mode = 5\n\n[node name=\"MouseSideB\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\ntexture = ExtResource(\"7_eseen\")\nstretch_mode = 5\n\n[node name=\"MouseCursor\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\ntexture = ExtResource(\"8_gauyw\")\nstretch_mode = 5\n\n[node name=\"Directions\" type=\"MarginContainer\" parent=\".\"]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\n\n[node name=\"Left\" type=\"TextureRect\" parent=\"Directions\"]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\ntexture = ExtResource(\"9_xglas\")\nstretch_mode = 5\n\n[node name=\"Right\" type=\"TextureRect\" parent=\"Directions\"]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\ntexture = ExtResource(\"10_8jstk\")\nstretch_mode = 5\n\n[node name=\"Up\" type=\"TextureRect\" parent=\"Directions\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntexture = ExtResource(\"11_edlod\")\nstretch_mode = 5\n\n[node name=\"Down\" type=\"TextureRect\" parent=\"Directions\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntexture = ExtResource(\"12_f7esb\")\nstretch_mode = 5\n\n[node name=\"Horizontal\" type=\"TextureRect\" parent=\"Directions\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntexture = ExtResource(\"13_8atfk\")\nstretch_mode = 5\n\n[node name=\"Vertical\" type=\"TextureRect\" parent=\"Directions\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntexture = ExtResource(\"14_5j0sm\")\nstretch_mode = 5\n"
  },
  {
    "path": "addons/guide/ui/renderers/mouse/styles/Mouse_Cursor.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://ci7icm3q4l1sg\"\npath=\"res://.godot/imported/Mouse_Cursor.png-4496bba3dd354c475ea32ff49976992d.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/mouse/styles/Mouse_Cursor.png\"\ndest_files=[\"res://.godot/imported/Mouse_Cursor.png-4496bba3dd354c475ea32ff49976992d.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/mouse/styles/Mouse_Left_Key_Light.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://vvgpheda22ew\"\npath=\"res://.godot/imported/Mouse_Left_Key_Light.png-8e9ba1443eb3f9bb7bd26ae3273c1598.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/mouse/styles/Mouse_Left_Key_Light.png\"\ndest_files=[\"res://.godot/imported/Mouse_Left_Key_Light.png-8e9ba1443eb3f9bb7bd26ae3273c1598.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/mouse/styles/Mouse_Middle_Key_Light.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bmj244x0jn7v2\"\npath=\"res://.godot/imported/Mouse_Middle_Key_Light.png-492b04eb649e2023aebd614b68eefdb3.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/mouse/styles/Mouse_Middle_Key_Light.png\"\ndest_files=[\"res://.godot/imported/Mouse_Middle_Key_Light.png-492b04eb649e2023aebd614b68eefdb3.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/mouse/styles/Mouse_Right_Key_Light.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://b8bsyguf4qw6f\"\npath=\"res://.godot/imported/Mouse_Right_Key_Light.png-6e3b9fc0d3705e025bfcc2928906f0cf.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/mouse/styles/Mouse_Right_Key_Light.png\"\ndest_files=[\"res://.godot/imported/Mouse_Right_Key_Light.png-6e3b9fc0d3705e025bfcc2928906f0cf.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/mouse/styles/Mouse_Side_Key_1_Light.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://je8rm7jk2nxd\"\npath=\"res://.godot/imported/Mouse_Side_Key_1_Light.png-56f79423bc604dbd79c4eacba321fc53.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/mouse/styles/Mouse_Side_Key_1_Light.png\"\ndest_files=[\"res://.godot/imported/Mouse_Side_Key_1_Light.png-56f79423bc604dbd79c4eacba321fc53.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/mouse/styles/Mouse_Side_Key_2_Light.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bqxly0g8pftxa\"\npath=\"res://.godot/imported/Mouse_Side_Key_2_Light.png-9b567e6de1156127ba1b79f3c5fe0fdd.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/mouse/styles/Mouse_Side_Key_2_Light.png\"\ndest_files=[\"res://.godot/imported/Mouse_Side_Key_2_Light.png-9b567e6de1156127ba1b79f3c5fe0fdd.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/mouse/styles/Mouse_Simple_Key_Light.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://b3uxk5agbpmab\"\npath=\"res://.godot/imported/Mouse_Simple_Key_Light.png-c104d4f498e18854caea792ec42ad1ae.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/mouse/styles/Mouse_Simple_Key_Light.png\"\ndest_files=[\"res://.godot/imported/Mouse_Simple_Key_Light.png-c104d4f498e18854caea792ec42ad1ae.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/mouse/styles/default.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMouseRenderStyle\" load_steps=15 format=3 uid=\"uid://cmbcfs6ubll5t\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/ui/renderers/mouse/guide_mouse_render_style.gd\" id=\"1_qsbcc\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://oq2vvwgbdsh7\" path=\"res://addons/guide/ui/renderers/textures/arrow_down.svg\" id=\"1_snv1n\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bmgxqbypegjxh\" path=\"res://addons/guide/ui/renderers/textures/arrow_horizontal.svg\" id=\"2_7emdp\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://1swh072gtbb4\" path=\"res://addons/guide/ui/renderers/textures/arrow_left.svg\" id=\"3_jmgjr\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://b3uxk5agbpmab\" path=\"res://addons/guide/ui/renderers/mouse/styles/Mouse_Simple_Key_Light.png\" id=\"4_bgsia\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://ci7icm3q4l1sg\" path=\"res://addons/guide/ui/renderers/mouse/styles/Mouse_Cursor.png\" id=\"5_4ulvd\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://vvgpheda22ew\" path=\"res://addons/guide/ui/renderers/mouse/styles/Mouse_Left_Key_Light.png\" id=\"6_7jnra\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bmj244x0jn7v2\" path=\"res://addons/guide/ui/renderers/mouse/styles/Mouse_Middle_Key_Light.png\" id=\"7_1twqf\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://b8bsyguf4qw6f\" path=\"res://addons/guide/ui/renderers/mouse/styles/Mouse_Right_Key_Light.png\" id=\"8_52mli\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://je8rm7jk2nxd\" path=\"res://addons/guide/ui/renderers/mouse/styles/Mouse_Side_Key_1_Light.png\" id=\"9_4wuxg\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bqxly0g8pftxa\" path=\"res://addons/guide/ui/renderers/mouse/styles/Mouse_Side_Key_2_Light.png\" id=\"10_fmy7r\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://cjvs04qsrj8ap\" path=\"res://addons/guide/ui/renderers/textures/arrow_right.svg\" id=\"11_hfko8\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://ni6lsbx1d2hf\" path=\"res://addons/guide/ui/renderers/textures/arrow_up.svg\" id=\"13_6lqqf\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bu5nlug6uf03w\" path=\"res://addons/guide/ui/renderers/textures/arrow_vertical.svg\" id=\"14_vttvb\"]\n\n[resource]\nscript = ExtResource(\"1_qsbcc\")\nmouse_blank = ExtResource(\"4_bgsia\")\nmouse_left = ExtResource(\"6_7jnra\")\nmouse_right = ExtResource(\"8_52mli\")\nmouse_middle = ExtResource(\"7_1twqf\")\nmouse_side_a = ExtResource(\"9_4wuxg\")\nmouse_side_b = ExtResource(\"10_fmy7r\")\nmouse_cursor = ExtResource(\"5_4ulvd\")\nleft = ExtResource(\"3_jmgjr\")\nright = ExtResource(\"11_hfko8\")\nup = ExtResource(\"13_6lqqf\")\ndown = ExtResource(\"1_snv1n\")\nhorizontal = ExtResource(\"2_7emdp\")\nvertical = ExtResource(\"14_vttvb\")\n"
  },
  {
    "path": "addons/guide/ui/renderers/style.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://dx2f7nye50biq\"\npath=\"res://.godot/imported/style.svg-351ded8bde5ee9f4f328e787a5fe5348.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/style.svg\"\ndest_files=[\"res://.godot/imported/style.svg-351ded8bde5ee9f4f328e787a5fe5348.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/ui/renderers/textures/action.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://diwkvjkss2ie\"\npath=\"res://.godot/imported/action.svg-6100da2ab8ea5d289c6e91ccdfb53aca.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/textures/action.svg\"\ndest_files=[\"res://.godot/imported/action.svg-6100da2ab8ea5d289c6e91ccdfb53aca.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=false\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/ui/renderers/textures/arrow_all_directions.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://dwb1h8sfsccyy\"\npath=\"res://.godot/imported/arrow_all_directions.svg-c87a4938e66e69435ad57c677b38771f.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/textures/arrow_all_directions.svg\"\ndest_files=[\"res://.godot/imported/arrow_all_directions.svg-c87a4938e66e69435ad57c677b38771f.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=false\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/ui/renderers/textures/arrow_down.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://oq2vvwgbdsh7\"\npath=\"res://.godot/imported/arrow_down.svg-88a3b47c68c37638cef21944ad9cda50.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/textures/arrow_down.svg\"\ndest_files=[\"res://.godot/imported/arrow_down.svg-88a3b47c68c37638cef21944ad9cda50.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=false\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/ui/renderers/textures/arrow_horizontal.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bmgxqbypegjxh\"\npath=\"res://.godot/imported/arrow_horizontal.svg-5fd469f78a3e46cba20723a7b243bca1.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/textures/arrow_horizontal.svg\"\ndest_files=[\"res://.godot/imported/arrow_horizontal.svg-5fd469f78a3e46cba20723a7b243bca1.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=false\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/ui/renderers/textures/arrow_left.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://1swh072gtbb4\"\npath=\"res://.godot/imported/arrow_left.svg-2a189e6eec3713a64220cf9427e1f45c.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/textures/arrow_left.svg\"\ndest_files=[\"res://.godot/imported/arrow_left.svg-2a189e6eec3713a64220cf9427e1f45c.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=false\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/ui/renderers/textures/arrow_right.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cjvs04qsrj8ap\"\npath=\"res://.godot/imported/arrow_right.svg-83b2fe427227f253ed212a8b1c56acb4.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/textures/arrow_right.svg\"\ndest_files=[\"res://.godot/imported/arrow_right.svg-83b2fe427227f253ed212a8b1c56acb4.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=false\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/ui/renderers/textures/arrow_up.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://ni6lsbx1d2hf\"\npath=\"res://.godot/imported/arrow_up.svg-56e16fd95d307eb9666c8ac4e78e2b97.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/textures/arrow_up.svg\"\ndest_files=[\"res://.godot/imported/arrow_up.svg-56e16fd95d307eb9666c8ac4e78e2b97.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=false\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/ui/renderers/textures/arrow_vertical.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bu5nlug6uf03w\"\npath=\"res://.godot/imported/arrow_vertical.svg-17983361d36ac9313b8d80f7240cf6aa.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/textures/arrow_vertical.svg\"\ndest_files=[\"res://.godot/imported/arrow_vertical.svg-17983361d36ac9313b8d80f7240cf6aa.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=false\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/ui/renderers/touch/guide_touch_render_style.gd",
    "content": "@tool\n@icon(\"../style.svg\")\n## A render style for the touch renderer.\nclass_name GUIDETouchRenderStyle\nextends Resource\n\n## An icon indicating touch with one finger.\n@export var one_finger:Texture2D\n\n## An icon indicating touch with two fingers.\n@export var two_fingers:Texture2D\n\n## An icon indicating touch with three fingers.\n@export var three_fingers:Texture2D\n\n## An icon indicating touch with four fingers.\n@export var four_fingers:Texture2D\n\n## An icon indicating a rotating gesture.\n@export var rotate:Texture2D\n\n## An icon indicating a zoom gesture.\n@export var zoom:Texture2D\n\n\n@export_category(\"Directions\")\n\n## An icon indicating horizontal movement.\n@export var horizontal:Texture2D\n\n## An icon indicating vertical movement.\n@export var vertical:Texture2D\n\n## An icon indicating both horizontal and vertical movement.\n@export var both:Texture2D\n"
  },
  {
    "path": "addons/guide/ui/renderers/touch/guide_touch_render_style.gd.uid",
    "content": "uid://c22c07tt7y1x3\n"
  },
  {
    "path": "addons/guide/ui/renderers/touch/guide_touch_renderer.gd",
    "content": "@tool\nclass_name GUIDETouchRenderer\nextends GUIDEIconRenderer\n\n@onready var _controls:Control = %Controls\n@onready var _1_finger:TextureRect = %T1Finger\n@onready var _2_finger:TextureRect = %T2Fingers\n@onready var _3_finger:TextureRect = %T3Fingers\n@onready var _4_finger:TextureRect = %T4Fingers\n@onready var _rotate:TextureRect = %Rotate\n@onready var _zoom:TextureRect = %Zoom\n\n\n@onready var _directions:Control = %Directions\n@onready var _horizontal:TextureRect = %Horizontal\n@onready var _vertical:TextureRect = %Vertical\n@onready var _axis2d:TextureRect = %Axis2D\n\n\nstatic var _style:GUIDETouchRenderStyle = preload(\"styles/default.tres\")\n\n## Sets the style to be used by this renderer.\nstatic func set_style(style:GUIDETouchRenderStyle) -> void:\n\tif not is_instance_valid(style):\n\t\tpush_error(\"Touch style must not be null.\")\n\t\treturn\n\t_style = style\n\t\n\nfunc supports(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> bool:\n\treturn input is GUIDEInputTouchAxis1D or \\\n\t\tinput is GUIDEInputTouchAxis2D or \\\n\t\tinput is GUIDEInputTouchPosition or \\\n\t\tinput is GUIDEInputTouchAngle or \\\n\t\tinput is GUIDEInputTouchDistance\n\t\t\n\t\n\t\nfunc render(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> void:\n\tfor child in _controls.get_children():\n\t\tchild.visible = false\n\tfor child in _directions.get_children():\n\t\tchild.visible = false\n\t\t\n\t_directions.visible = false\n\t\n\t_1_finger.texture = _style.one_finger\n\t_2_finger.texture = _style.two_fingers\n\t_3_finger.texture = _style.three_fingers\n\t_4_finger.texture = _style.four_fingers\n\t_rotate.texture = _style.rotate\n\t_zoom.texture = _style.zoom\n\t_horizontal.texture = _style.horizontal\n\t_vertical.texture = _style.vertical\n\t_axis2d.texture = _style.both\n\t\t\n\tif input is GUIDEInputTouchBase:\n\t\tmatch input.finger_count:\n\t\t\t2:\n\t\t\t\t_2_finger.visible = true\n\t\t\t3:\n\t\t\t\t_3_finger.visible = true\n\t\t\t4:\n\t\t\t\t_4_finger.visible = true\n\t\t\t_:\n\t\t\t\t# we have no icons for more than 4 fingers, so everything else gets\n\t\t\t\t# the 1 finger icon\n\t\t\t\t_1_finger.visible = true\t\n\t\t\n\tif input is GUIDEInputTouchAxis2D:\n\t\t_directions.visible = true\n\t\t_axis2d.visible = true\n\t\t\n\tif input is GUIDEInputTouchAxis1D:\n\t\t_directions.visible = true\n\t\tmatch input.axis:\n\t\t\tGUIDEInputTouchAxis1D.GUIDEInputTouchAxis.X:\n\t\t\t\t_horizontal.visible = true\n\t\t\tGUIDEInputTouchAxis1D.GUIDEInputTouchAxis.X:\n\t\t\t\t_vertical.visible = true\n\t\t\t\t\n\tif input is GUIDEInputTouchDistance:\n\t\t_zoom.visible = true\n\t\t\n\tif input is GUIDEInputTouchAngle:\n\t\t_rotate.visible = true\n\t\t\n\tcall(\"queue_sort\")\n \nfunc cache_key(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> String:\n\t# output depends on input and the style\n\treturn \"1f4c5082-d419-465f-aba8-f889caaff335\" \\\n\t\t+ input.to_string() \\\n\t\t+ _style.resource_path\n"
  },
  {
    "path": "addons/guide/ui/renderers/touch/guide_touch_renderer.gd.uid",
    "content": "uid://ey7xnkw46a6js\n"
  },
  {
    "path": "addons/guide/ui/renderers/touch/guide_touch_renderer.tscn",
    "content": "[gd_scene load_steps=11 format=3 uid=\"uid://ykuou1deo5ub\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/ui/renderers/touch/guide_touch_renderer.gd\" id=\"1_wulrq\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://c5nwnp5cjny7m\" path=\"res://addons/guide/ui/renderers/touch/styles/touch_1_finger.png\" id=\"2_b6hdr\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bllhe78a1yo6\" path=\"res://addons/guide/ui/renderers/touch/styles/touch_2_fingers.png\" id=\"3_hk3mb\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bwhqf2nmm5q1w\" path=\"res://addons/guide/ui/renderers/touch/styles/touch_3_fingers.png\" id=\"4_swfqu\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://cjw5m42gufghr\" path=\"res://addons/guide/ui/renderers/touch/styles/touch_4_fingers.png\" id=\"5_b7wc6\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bxj4t5vjx7o3w\" path=\"res://addons/guide/ui/renderers/touch/styles/touch_rotate.png\" id=\"6_m5fhs\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://cutplj0nhphk\" path=\"res://addons/guide/ui/renderers/touch/styles/touch_zoom.png\" id=\"7_q2tlp\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bmgxqbypegjxh\" path=\"res://addons/guide/ui/renderers/textures/arrow_horizontal.svg\" id=\"8_v744w\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bu5nlug6uf03w\" path=\"res://addons/guide/ui/renderers/textures/arrow_vertical.svg\" id=\"9_yug52\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://dwb1h8sfsccyy\" path=\"res://addons/guide/ui/renderers/textures/arrow_all_directions.svg\" id=\"10_w0ld1\"]\n\n[node name=\"TouchRenderer\" type=\"MarginContainer\"]\nprocess_mode = 3\ncustom_minimum_size = Vector2(128, 128)\noffset_right = 100.0\noffset_bottom = 100.0\nsize_flags_horizontal = 0\nscript = ExtResource(\"1_wulrq\")\n\n[node name=\"Controls\" type=\"MarginContainer\" parent=\".\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntheme_override_constants/margin_left = 14\ntheme_override_constants/margin_top = 14\ntheme_override_constants/margin_right = 14\ntheme_override_constants/margin_bottom = 14\n\n[node name=\"T1Finger\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\ntexture = ExtResource(\"2_b6hdr\")\nstretch_mode = 5\n\n[node name=\"T2Fingers\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\ntexture = ExtResource(\"3_hk3mb\")\nstretch_mode = 5\n\n[node name=\"T3Fingers\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\ntexture = ExtResource(\"4_swfqu\")\nstretch_mode = 5\n\n[node name=\"T4Fingers\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\ntexture = ExtResource(\"5_b7wc6\")\nstretch_mode = 5\n\n[node name=\"Rotate\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\ntexture = ExtResource(\"6_m5fhs\")\nstretch_mode = 5\n\n[node name=\"Zoom\" type=\"TextureRect\" parent=\"Controls\"]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\ntexture = ExtResource(\"7_q2tlp\")\nstretch_mode = 5\n\n[node name=\"Directions\" type=\"MarginContainer\" parent=\".\"]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\n\n[node name=\"Horizontal\" type=\"TextureRect\" parent=\"Directions\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntexture = ExtResource(\"8_v744w\")\nstretch_mode = 5\n\n[node name=\"Vertical\" type=\"TextureRect\" parent=\"Directions\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntexture = ExtResource(\"9_yug52\")\nstretch_mode = 5\n\n[node name=\"Axis2D\" type=\"TextureRect\" parent=\"Directions\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntexture = ExtResource(\"10_w0ld1\")\nstretch_mode = 5\n"
  },
  {
    "path": "addons/guide/ui/renderers/touch/styles/default.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDETouchRenderStyle\" load_steps=11 format=3 uid=\"uid://c6em225fn0luc\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/ui/renderers/touch/guide_touch_render_style.gd\" id=\"1_dclrw\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://dwb1h8sfsccyy\" path=\"res://addons/guide/ui/renderers/textures/arrow_all_directions.svg\" id=\"1_quugf\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://cjw5m42gufghr\" path=\"res://addons/guide/ui/renderers/touch/styles/touch_4_fingers.png\" id=\"2_mf862\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bmgxqbypegjxh\" path=\"res://addons/guide/ui/renderers/textures/arrow_horizontal.svg\" id=\"3_biagq\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://c5nwnp5cjny7m\" path=\"res://addons/guide/ui/renderers/touch/styles/touch_1_finger.png\" id=\"4_v2uag\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bxj4t5vjx7o3w\" path=\"res://addons/guide/ui/renderers/touch/styles/touch_rotate.png\" id=\"5_to8ga\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bwhqf2nmm5q1w\" path=\"res://addons/guide/ui/renderers/touch/styles/touch_3_fingers.png\" id=\"7_r27vr\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bllhe78a1yo6\" path=\"res://addons/guide/ui/renderers/touch/styles/touch_2_fingers.png\" id=\"8_svelr\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bu5nlug6uf03w\" path=\"res://addons/guide/ui/renderers/textures/arrow_vertical.svg\" id=\"9_c28mq\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://cutplj0nhphk\" path=\"res://addons/guide/ui/renderers/touch/styles/touch_zoom.png\" id=\"10_3pu8f\"]\n\n[resource]\nscript = ExtResource(\"1_dclrw\")\none_finger = ExtResource(\"4_v2uag\")\ntwo_fingers = ExtResource(\"8_svelr\")\nthree_fingers = ExtResource(\"7_r27vr\")\nfour_fingers = ExtResource(\"2_mf862\")\nrotate = ExtResource(\"5_to8ga\")\nzoom = ExtResource(\"10_3pu8f\")\nhorizontal = ExtResource(\"3_biagq\")\nvertical = ExtResource(\"9_c28mq\")\nboth = ExtResource(\"1_quugf\")\n"
  },
  {
    "path": "addons/guide/ui/renderers/touch/styles/touch_1_finger.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://c5nwnp5cjny7m\"\npath=\"res://.godot/imported/touch_1_finger.png-9b97d7acf504be4b0510f47d4eaa65e9.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/touch/styles/touch_1_finger.png\"\ndest_files=[\"res://.godot/imported/touch_1_finger.png-9b97d7acf504be4b0510f47d4eaa65e9.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/touch/styles/touch_2_fingers.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bllhe78a1yo6\"\npath=\"res://.godot/imported/touch_2_fingers.png-d8555c5d1178f86701ceb2669c1cd1d4.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/touch/styles/touch_2_fingers.png\"\ndest_files=[\"res://.godot/imported/touch_2_fingers.png-d8555c5d1178f86701ceb2669c1cd1d4.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/touch/styles/touch_3_fingers.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bwhqf2nmm5q1w\"\npath=\"res://.godot/imported/touch_3_fingers.png-a9dcef9909d7cdf3676037c928eae64e.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/touch/styles/touch_3_fingers.png\"\ndest_files=[\"res://.godot/imported/touch_3_fingers.png-a9dcef9909d7cdf3676037c928eae64e.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/touch/styles/touch_4_fingers.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cjw5m42gufghr\"\npath=\"res://.godot/imported/touch_4_fingers.png-9c583b818e75cc396c6369c9bc7f8154.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/touch/styles/touch_4_fingers.png\"\ndest_files=[\"res://.godot/imported/touch_4_fingers.png-9c583b818e75cc396c6369c9bc7f8154.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/touch/styles/touch_rotate.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bxj4t5vjx7o3w\"\npath=\"res://.godot/imported/touch_rotate.png-41d457c209d69597ba922aa9b6bc3cc5.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/touch/styles/touch_rotate.png\"\ndest_files=[\"res://.godot/imported/touch_rotate.png-41d457c209d69597ba922aa9b6bc3cc5.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/renderers/touch/styles/touch_zoom.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cutplj0nhphk\"\npath=\"res://.godot/imported/touch_zoom.png-af5b89ea602e0236b8e51d36e80056ab.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/renderers/touch/styles/touch_zoom.png\"\ndest_files=[\"res://.godot/imported/touch_zoom.png-af5b89ea602e0236b8e51d36e80056ab.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "addons/guide/ui/text_providers/controllers/guide_controller_label_set.gd",
    "content": "@tool\nclass_name GUIDEControllerLabelSet\nextends Resource\n\n\n## Label for the bottom action button: Sony Cross, Xbox A, Nintendo B \n@export var a_button:String = \"\"\n## Label for the right action button: Sony Circle, Xbox B, Nintendo A.\n@export var b_button:String = \"\"\n## Label for the left action button: Sony Square, Xbox X, Nintendo Y.\n@export var x_button:String = \"\"\n## Label for the top action button: Sony Triangle, Xbox Y, Nintendo X.\n@export var y_button:String = \"\"\n## Label for the left stick of the controller. \n@export var left_stick:String = \"Left Stick\"\n## Label for the left stick click button of the controller. \n@export var left_stick_click:String = \"\"\n## Label for moving the left stick horizontally.\n@export var left_stick_horizontal_movement:String = \"Left Stick Horizontal\"\n## Label for moving the left stick verticall.\n@export var left_stick_vertical_movement:String = \"Left Stick Horizontal\"\n## Label for the right stick of the controller. \n@export var right_stick:String = \"Right Stick\"\n## Label for the right stick click button of the controller. \n@export var right_stick_click:String = \"\"\n## Label for moving the right stick horizontally.\n@export var right_stick_horizontal_movement:String = \"Left Stick Horizontal\"\n## Label for moving the right stick verticall.\n@export var right_stick_vertical_movement:String = \"Left Stick Horizontal\"\n## Label for the  left shoulder button: Sony L1, Xbox LB button.\n@export var left_bumper:String = \"\"\n## Label for the right shoulder button: Sony R1, XBOX RB button.\n@export var right_bumper:String = \"\"\n## Label for the left trigger. \n@export var left_trigger:String = \"\"\n## Label for the right trigger. \n@export var right_trigger:String = \"\"\n## Label for the up direction on the directional pad. \n@export var dpad_up:String = \"DPAD Up\"\n## Label for the left direction on the directional pad. \n@export var dpad_left:String = \"DPAD Left\"\n## Label for the right direction on the directional pad. \n@export var dpad_right:String = \"DPAD Right\"\n## Label for the down direction on the directional pad. \n@export var dpad_down:String = \"DPAD Down\"\n## Label for the Start button: Sony Options, Xbox Menu, Nintendo + button \n@export var start:String = \"\"\n## Label for the Miscellaneous 1 button: Xbox share , PS5 microphone , Nintendo Switch capture.\n@export var misc1:String = \"\"\n## Label for the back button:  Sony Select, Xbox Back, Nintendo - button\n@export var back:String = \"\"\n## Label for the touchpad.\n@export var touch_pad:String = \"\"\n"
  },
  {
    "path": "addons/guide/ui/text_providers/controllers/guide_controller_label_set.gd.uid",
    "content": "uid://bum53hjilsbma\n"
  },
  {
    "path": "addons/guide/ui/text_providers/controllers/guide_controller_text_provider.gd",
    "content": "@tool\nclass_name GUIDEControllerTextProvider\nextends GUIDETextProvider\n\nconst GUIDEFormattingUtils = preload(\"../../guide_formatting_utils.gd\")\nconst ControllerType = GUIDEFormattingUtils.ControllerType\n\n## Label sets that are used for each controller type. Key is controller type,\n## value is the label set.\nstatic var _controller_label_sets:Dictionary = {\n\tControllerType.MICROSOFT: preload(\"label_sets/microsoft.tres\"),\n\tControllerType.NINTENDO: preload(\"label_sets/nintendo.tres\"),\n\tControllerType.SONY: preload(\"label_sets/sony.tres\"),\n\tControllerType.STEAM_DECK: preload(\"label_sets/steam_deck.tres\"),\n}\n\nfunc _init():\n\tpriority = -10\n\t\n\t\n## Sets the label set that is used for providing texts of the given controller type. \n## Note that label sets are currently experimental and may disappear without notice.\t\nstatic func set_label_set(type:ControllerType, label_set:GUIDEControllerLabelSet) -> void:\n\tif not is_instance_valid(label_set):\n\t\tpush_error(\"Label set must not be null.\")\n\t\treturn\n\t\n\tif type == ControllerType.UNKNOWN:\n\t\tpush_error(\"Cannot set label set for the unknown controller. Use GUIDEInputFormattingOptions to set up how unknown controllers are rendered.\")\t\n\t\treturn\n\t\t\n\t_controller_label_sets[type] = label_set\n\t\nfunc supports(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> bool:\n\t# we support rendering if the input effectively can be mapped to any known\n\t# controller type using the options\n\treturn GUIDEFormattingUtils.effective_controller_type(input, options) != ControllerType.UNKNOWN\n\n\nfunc _format(input:String) -> String:\n\treturn \"[%s]\" % [input]\n\n\t\nfunc get_text(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> String:\n\tvar controller_type := GUIDEFormattingUtils.effective_controller_type(input, options)\n\t\n\tvar label_set:GUIDEControllerLabelSet = _controller_label_sets[controller_type]\n\t\n\tif input is GUIDEInputJoyAxis1D:\n\t\tmatch input.axis:\n\t\t\tJOY_AXIS_LEFT_X:\n\t\t\t\treturn _format(tr(label_set.left_stick_horizontal_movement))\n\t\t\tJOY_AXIS_LEFT_Y:\n\t\t\t\treturn _format(tr(label_set.left_stick_vertical_movement))\n\t\t\tJOY_AXIS_RIGHT_X:\n\t\t\t\treturn _format(tr(label_set.right_stick_horizontal_movement))\n\t\t\tJOY_AXIS_RIGHT_Y:\n\t\t\t\treturn _format(tr(label_set.right_stick_vertical_movement))\n\t\t\tJOY_AXIS_TRIGGER_LEFT:\n\t\t\t\treturn _format(tr(label_set.left_trigger))\n\t\t\tJOY_AXIS_TRIGGER_RIGHT:\n\t\t\t\treturn _format(tr(label_set.right_trigger))\n\t\n\tif input is GUIDEInputJoyAxis2D:\n\t\tmatch input.x:\n\t\t\tJOY_AXIS_LEFT_X, JOY_AXIS_LEFT_Y:\n\t\t\t\treturn _format(tr(label_set.left_stick))\n\t\t\tJOY_AXIS_RIGHT_X, JOY_AXIS_RIGHT_Y:\n\t\t\t\treturn _format(tr(label_set.right_stick))\n\t\t\t\t\n\tif input is GUIDEInputJoyButton:\n\t\tmatch input.button:\n\t\t\tJOY_BUTTON_A:\n\t\t\t\treturn _format(tr(label_set.a_button))\n\t\t\tJOY_BUTTON_B:\n\t\t\t\treturn _format(tr(label_set.b_button))\n\t\t\tJOY_BUTTON_X:\n\t\t\t\treturn _format(tr(label_set.x_button))\n\t\t\tJOY_BUTTON_Y:\n\t\t\t\treturn _format(tr(label_set.y_button))\n\t\t\tJOY_BUTTON_DPAD_LEFT:\n\t\t\t\treturn _format(tr(label_set.dpad_left))\n\t\t\tJOY_BUTTON_DPAD_RIGHT:\n\t\t\t\treturn _format(tr(label_set.dpad_right))\n\t\t\tJOY_BUTTON_DPAD_UP:\n\t\t\t\treturn _format(tr(label_set.dpad_up))\n\t\t\tJOY_BUTTON_DPAD_DOWN:\n\t\t\t\treturn _format(tr(label_set.dpad_down))\n\t\t\tJOY_BUTTON_LEFT_SHOULDER:\n\t\t\t\treturn _format(tr(label_set.left_bumper))\t\n\t\t\tJOY_BUTTON_RIGHT_SHOULDER:\n\t\t\t\treturn _format(tr(label_set.right_bumper))\t\n\t\t\tJOY_BUTTON_LEFT_STICK:\n\t\t\t\treturn _format(tr(label_set.left_stick_click))\t\n\t\t\tJOY_BUTTON_RIGHT_STICK:\n\t\t\t\treturn _format(tr(label_set.right_stick_click))\t\n\t\t\tJOY_BUTTON_BACK:\n\t\t\t\treturn _format(tr(label_set.back))\n\t\t\tJOY_BUTTON_MISC1:\n\t\t\t\treturn _format(tr(label_set.misc1))\n\t\t\tJOY_BUTTON_START:\n\t\t\t\treturn _format(tr(label_set.start))\t\n\t\t\tJOY_BUTTON_TOUCHPAD:\n\t\t\t\treturn _format(tr(label_set.touch_pad))\t\n\n\treturn _format(\"??\")\n"
  },
  {
    "path": "addons/guide/ui/text_providers/controllers/guide_controller_text_provider.gd.uid",
    "content": "uid://bk5417v12jfbt\n"
  },
  {
    "path": "addons/guide/ui/text_providers/controllers/label_sets/microsoft.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEControllerLabelSet\" load_steps=2 format=3 uid=\"uid://dwexh6qbqas2t\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/ui/text_providers/controllers/guide_controller_label_set.gd\" id=\"1_shtmd\"]\n\n[resource]\nscript = ExtResource(\"1_shtmd\")\na_button = \"A\"\nb_button = \"B\"\nx_button = \"C\"\ny_button = \"X\"\nleft_stick = \"Left Stick\"\nleft_stick_click = \"Left Stick Click\"\nleft_stick_horizontal_movement = \"Left Stick Horizontal\"\nleft_stick_vertical_movement = \"Left Stick Horizontal\"\nright_stick = \"Right Stick\"\nright_stick_click = \"Right Stick Click\"\nright_stick_horizontal_movement = \"Left Stick Horizontal\"\nright_stick_vertical_movement = \"Left Stick Horizontal\"\nleft_bumper = \"LB\"\nright_bumper = \"RB\"\nleft_trigger = \"LT\"\nright_trigger = \"RT\"\ndpad_up = \"DPAD Up\"\ndpad_left = \"DPAD Left\"\ndpad_right = \"DPAD Right\"\ndpad_down = \"DPAD Down\"\nstart = \"Menu\"\nmisc1 = \"Share\"\nback = \"View\"\ntouch_pad = \"??\"\n"
  },
  {
    "path": "addons/guide/ui/text_providers/controllers/label_sets/nintendo.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEControllerLabelSet\" load_steps=2 format=3 uid=\"uid://b01nntj4s1cyy\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/ui/text_providers/controllers/guide_controller_label_set.gd\" id=\"1_5cwdo\"]\n\n[resource]\nscript = ExtResource(\"1_5cwdo\")\na_button = \"B\"\nb_button = \"A\"\nx_button = \"Y\"\ny_button = \"X\"\nleft_stick = \"Left Stick\"\nleft_stick_click = \"Left Stick Click\"\nleft_stick_horizontal_movement = \"Left Stick Horizontal\"\nleft_stick_vertical_movement = \"Left Stick Horizontal\"\nright_stick = \"Right Stick\"\nright_stick_click = \"Left Stick Click\"\nright_stick_horizontal_movement = \"Left Stick Horizontal\"\nright_stick_vertical_movement = \"Left Stick Horizontal\"\nleft_bumper = \"L\"\nright_bumper = \"R\"\nleft_trigger = \"ZL\"\nright_trigger = \"ZR\"\ndpad_up = \"DPAD Up\"\ndpad_left = \"DPAD Left\"\ndpad_right = \"DPAD Right\"\ndpad_down = \"DPAD Down\"\nstart = \"+\"\nmisc1 = \"Capture\"\nback = \"-\"\ntouch_pad = \"??\"\n"
  },
  {
    "path": "addons/guide/ui/text_providers/controllers/label_sets/sony.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEControllerLabelSet\" load_steps=2 format=3 uid=\"uid://bhk8il4hvogom\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/ui/text_providers/controllers/guide_controller_label_set.gd\" id=\"1_xmd61\"]\n\n[resource]\nscript = ExtResource(\"1_xmd61\")\na_button = \"Cross\"\nb_button = \"Circle\"\nx_button = \"Square\"\ny_button = \"Triangle\"\nleft_stick = \"Left Stick\"\nleft_stick_click = \"Left Stick Click\"\nleft_stick_horizontal_movement = \"Left Stick Horizontal\"\nleft_stick_vertical_movement = \"Left Stick Horizontal\"\nright_stick = \"Right Stick\"\nright_stick_click = \"Left Stick Click\"\nright_stick_horizontal_movement = \"Left Stick Horizontal\"\nright_stick_vertical_movement = \"Left Stick Horizontal\"\nleft_bumper = \"L1\"\nright_bumper = \"R1\"\nleft_trigger = \"L2\"\nright_trigger = \"R2\"\ndpad_up = \"DPAD Up\"\ndpad_left = \"DPAD Left\"\ndpad_right = \"DPAD Right\"\ndpad_down = \"DPAD Down\"\nstart = \"Options\"\nmisc1 = \"Microphone\"\nback = \"Share\"\ntouch_pad = \"Touchpad\"\n"
  },
  {
    "path": "addons/guide/ui/text_providers/controllers/label_sets/steam_deck.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEControllerLabelSet\" load_steps=2 format=3 uid=\"uid://vbfu62vlwusm\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/ui/text_providers/controllers/guide_controller_label_set.gd\" id=\"1_4mq1b\"]\n\n[resource]\nscript = ExtResource(\"1_4mq1b\")\na_button = \"A\"\nb_button = \"B\"\nx_button = \"C\"\ny_button = \"X\"\nleft_stick = \"Left Stick\"\nleft_stick_click = \"Left Stick Click\"\nleft_stick_horizontal_movement = \"Left Stick Horizontal\"\nleft_stick_vertical_movement = \"Left Stick Horizontal\"\nright_stick = \"Right Stick\"\nright_stick_click = \"Right Stick Click\"\nright_stick_horizontal_movement = \"Left Stick Horizontal\"\nright_stick_vertical_movement = \"Left Stick Horizontal\"\nleft_bumper = \"L1\"\nright_bumper = \"R1\"\nleft_trigger = \"L2\"\nright_trigger = \"R2\"\ndpad_up = \"DPAD Up\"\ndpad_left = \"DPAD Left\"\ndpad_right = \"DPAD Right\"\ndpad_down = \"DPAD Down\"\nstart = \"??\"\nmisc1 = \"??\"\nback = \"??\"\ntouch_pad = \"??\"\n"
  },
  {
    "path": "addons/guide/ui/text_providers/default_text_provider.gd",
    "content": "extends GUIDETextProvider\n\nvar _is_on_desktop:bool = false\n\nfunc _init():\n\tpriority = 0\n\t_is_on_desktop = OS.has_feature(\"linuxbsd\") or OS.has_feature(\"macos\") or OS.has_feature(\"windows\")\n\t\nfunc supports(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> bool:\n\treturn true\n\t\n\nfunc _format(input:String) -> String:\n\treturn \"[%s]\" % [input]\n\n\t\nfunc get_text(input:GUIDEInput, options:GUIDEInputFormattingOptions) -> String:\n\tif input is GUIDEInputKey:\n\t\tvar result:PackedStringArray = []\n\t\tvar the_key = input.key\n\t\t\n\t\t# if we are on desktop, translate the physical keycode into the actual label\n\t\t# this is not supported on mobile, so we have to check\t\t\n\t\tif _is_on_desktop:\n\t\t\tthe_key = DisplayServer.keyboard_get_label_from_physical(input.key)\n\t\t\t\n\t\t\n\t\tresult.append(_format(OS.get_keycode_string(the_key)))\n\t\treturn \"+\".join(result) \n\t\n\tif input is GUIDEInputMouseAxis1D:\n\t\tmatch input.axis:\n\t\t\tGUIDEInputMouseAxis1D.GUIDEInputMouseAxis.X:\n\t\t\t\treturn _format(tr(\"Mouse Left/Right\"))\n\t\t\tGUIDEInputMouseAxis1D.GUIDEInputMouseAxis.Y:\n\t\t\t\treturn _format(tr(\"Mouse Up/Down\"))\n\n\tif input is GUIDEInputMouseAxis2D:\n\t\treturn _format(tr(\"Mouse\"))\n\t\t\n\tif input is GUIDEInputMouseButton:\n\t\tmatch input.button:\n\t\t\tMOUSE_BUTTON_LEFT:\n\t\t\t\treturn _format(tr(\"Left Mouse Button\"))\n\t\t\tMOUSE_BUTTON_RIGHT:\n\t\t\t\treturn _format(tr(\"Right Mouse Button\"))\n\t\t\tMOUSE_BUTTON_MIDDLE:\n\t\t\t\treturn _format(tr(\"Middle Mouse Button\"))\n\t\t\tMOUSE_BUTTON_WHEEL_UP:\n\t\t\t\treturn _format(tr(\"Mouse Wheel Up\"))\n\t\t\tMOUSE_BUTTON_WHEEL_DOWN:\n\t\t\t\treturn _format(tr(\"Mouse Wheel Down\"))\n\t\t\tMOUSE_BUTTON_WHEEL_LEFT:\n\t\t\t\treturn _format(tr(\"Mouse Wheel Left\"))\n\t\t\tMOUSE_BUTTON_WHEEL_RIGHT:\n\t\t\t\treturn _format(tr(\"Mouse Wheel Right\"))\n\t\t\tMOUSE_BUTTON_XBUTTON1:\n\t\t\t\treturn _format(tr(\"Mouse Side 1\"))\n\t\t\tMOUSE_BUTTON_XBUTTON2:\n\t\t\t\treturn _format(tr(\"Mouse Side 2\"))\n\n\tif input is GUIDEInputJoyAxis1D:\n\t\tmatch input.axis:\n\t\t\tJOY_AXIS_LEFT_X:\n\t\t\t\treturn _format(tr(\"Stick 1 Horizontal\"))\n\t\t\tJOY_AXIS_LEFT_Y:\n\t\t\t\treturn _format(tr(\"Stick 1 Vertical\"))\n\t\t\tJOY_AXIS_RIGHT_X:\n\t\t\t\treturn _format(tr(\"Stick 2 Horizontal\"))\n\t\t\tJOY_AXIS_RIGHT_Y:\n\t\t\t\treturn _format(tr(\"Stick 2 Vertical\"))\n\t\t\tJOY_AXIS_TRIGGER_LEFT:\n\t\t\t\treturn _format(tr(\"Axis 3\"))\n\t\t\tJOY_AXIS_TRIGGER_RIGHT:\n\t\t\t\treturn _format(tr(\"Axis 4\"))\n\t\n\tif input is GUIDEInputJoyAxis2D:\n\t\tmatch input.x:\n\t\t\tJOY_AXIS_LEFT_X, JOY_AXIS_LEFT_Y:\n\t\t\t\treturn _format(tr(\"Stick 1\"))\n\t\t\tJOY_AXIS_RIGHT_X, JOY_AXIS_RIGHT_Y:\n\t\t\t\treturn _format(tr(\"Stick 2\"))\n\t\t\t\t\n\tif input is GUIDEInputJoyButton:\n\t\treturn _format(tr(\"Joy %s\") % [input.button])\n\t\t\n\t\t\n\tif input is GUIDEInputAction:\n\t\treturn _format(tr(\"Action %s\") % [\"?\" if input.action == null else input.action._editor_name()])\n\n\tif input is GUIDEInputAny:\n\t\tvar parts:Array[String] = []\n\t\tif input.joy:\n\t\t\tparts.append(tr(\"Joy Button\"))\n\t\tif input.mouse:\n\t\t\tparts.append(tr(\"Mouse Button\"))\n\t\tif input.keyboard:\n\t\t\tparts.append(tr(\"Key\"))\n\t\t\t\n\t\treturn _format(tr(\"Any %s\") % [ \"/\".join(parts) ] )\n\t\t\t\n\tif input is GUIDEInputMousePosition:\n\t\treturn _format(tr(\"Mouse Position\"))\n\t\t\n\tif input is GUIDEInputTouchPosition:\n\t\treturn _format(tr(\"Touch Position %s\") % [input.finger_index  if input.finger_index >= 0 else \"Average\"])\n\n\tif input is GUIDEInputTouchAngle:\n\t\treturn _format(tr(\"Touch Angle\"))\n\t\t\n\tif input is GUIDEInputTouchDistance:\n\t\treturn _format(tr(\"Touch Distance\"))\n\n\tif input is GUIDEInputTouchAxis1D:\n\t\tmatch input.axis:\n\t\t\tGUIDEInputTouchAxis1D.GUIDEInputTouchAxis.X:\n\t\t\t\treturn _format(tr(\"Touch Left/Right %s\") % [input.finger_index  if input.finger_index >= 0 else \"Average\"])\n\t\t\tGUIDEInputTouchAxis1D.GUIDEInputTouchAxis.Y:\n\t\t\t\treturn _format(tr(\"Touch Up/Down %s\") % [input.finger_index  if input.finger_index >= 0 else \"Average\"])\n\t\n\tif input is GUIDEInputTouchAxis2D:\n\t\treturn _format(tr(\"Touch Axis 2D %s\") % [input.finger_index  if input.finger_index >= 0 else \"Average\"])\n\n\treturn _format(\"??\")\n"
  },
  {
    "path": "addons/guide/ui/text_providers/default_text_provider.gd.uid",
    "content": "uid://cpsrogavf5hy7\n"
  },
  {
    "path": "addons/guide/ui/virtual_joy/guide_virtual_joy_base.gd",
    "content": "class_name GUIDEVirtualJoyBase\nextends CenterContainer\n\n## Called when the control's actuation state changes. Can be used to update\n## the rendering of the button on screen.\nsignal changed()\n\n## Called when the control's configuration changes. Can be used to re-initialize\n## renderers.\nsignal configuration_changed()\n\nenum InputMode {\n\t## Only react to touch input.\n\tTOUCH,\n\t## Only react to mouse input.\n\tMOUSE,\n\t## React to both touch and mouse input.\n\tMOUSE_AND_TOUCH\n}\n\n## Index of the virtual stick this should drive.\n@export_enum(\"Virtual 1:0\", \"Virtual 2:1\", \"Virtual 3:2\", \"Virtual 4:3\") var virtual_device: int = 0:\n\tset(value):\n\t\tvirtual_device = value\n\t\t_reconnect()\n\t\tconfiguration_changed.emit()\n\n## The input mode to use.\n@export var input_mode: InputMode = InputMode.TOUCH:\n\tset(value):\n\t\tinput_mode = value\n\t\tconfiguration_changed.emit()\n\n@export var draw_debug: bool = false:\n\tset(value):\n\t\tdraw_debug = value\n\t\tqueue_redraw()\n\t\tconfiguration_changed.emit()\n\nvar _is_actuated: bool = false\n\n## The virtual joy id assigned to this stick.\nvar _virtual_joy_id : int = 0\n\n## Connects this virtual device to the input system. If already connected,\n## it will first disconnect.\nfunc _reconnect() -> void:\t\n\tif not is_node_ready():\n\t\treturn\n\t\t\n\tif _virtual_joy_id != 0:\n\t\tGUIDE._input_state.disconnect_virtual_stick(_virtual_joy_id)\n\t\n\t_virtual_joy_id = GUIDE._input_state.connect_virtual_stick(virtual_device)\n\t\n\n## Handles input events. This is done here and not in the GUIDE input state\n## because this should behave like a control node and should consume input events\n## that it uses so they don't propagate to other nodes.\nfunc _input(event: InputEvent) -> void:\n\tif event is InputEventMouse:\n\t\tif input_mode == InputMode.MOUSE or input_mode == InputMode.MOUSE_AND_TOUCH:\n\t\t\t_handle_mouse_input(event)\n\n\tif event is InputEventScreenTouch or event is InputEventScreenDrag:\n\t\tif input_mode == InputMode.TOUCH or input_mode == InputMode.MOUSE_AND_TOUCH:\n\t\t\t_handle_touch_input(event)\n\n\n## Handles input from the mouse.\nfunc _handle_mouse_input(event: InputEventMouse) -> void:\n\tpass\n\t\n\t\n## Handles input from touch devices.\nfunc _handle_touch_input(event: InputEvent) -> void:\n\tpass\n\n## Converts screen coordinates to world coordinates.\nfunc _screen_to_world(input: Vector2) -> Vector2:\n\treturn get_canvas_transform().affine_inverse() * input\t\n"
  },
  {
    "path": "addons/guide/ui/virtual_joy/guide_virtual_joy_base.gd.uid",
    "content": "uid://2eeqipo6g0tv\n"
  },
  {
    "path": "addons/guide/ui/virtual_joy/virtual_button/guide_virtual_button.gd",
    "content": "@tool\n@icon(\"guide_virtual_button.svg\")\n## A virtual joystick button.\nclass_name GUIDEVirtualButton\nextends GUIDEVirtualJoyBase\n\n## Radius of the button.\n@export var button_radius: float = 100:\n\tset(value):\n\t\tbutton_radius = value\n\t\tqueue_redraw()\n\t\tconfiguration_changed.emit()\n\n## The joy button represented by this virtual button.\n@export var button_index: JoyButton = JOY_BUTTON_A:\n\tset(value):\n\t\tbutton_index = value\n\t\tconfiguration_changed.emit()\n\n## The finger positions that are currently touching the screen.\nvar _finger_positions:Dictionary = {}\n\n## Whether or not we consumed the mouse down event.\nvar _mouse_down_consumed: bool = false\n\nfunc _ready() -> void:\n\tuse_top_left = true\n\tmouse_filter = Control.MOUSE_FILTER_IGNORE\n\tsize = Vector2.ZERO\n\t# the editor likes to set the size to 40x40, we'll override that\n\tif Engine.is_editor_hint():\n\t\tset_deferred(\"size\", Vector2.ZERO)\n\t\t\n\t# let renderers know the initial configuration\n\tconfiguration_changed.emit()\n\t# and the initial state\n\tchanged.emit()\n\t\t\n\tif Engine.is_editor_hint():\n\t\treturn # no input processing in the editor\n\t\t\n\t_reconnect()\n\t_report_input()\n\t\n\n## Handles input from the mouse.\nfunc _handle_mouse_input(event:InputEventMouse) -> void:\n\tvar pos := _screen_to_world(event.position)\n\tif not _is_actuated:\n\t\tif event is InputEventMouseMotion:\n\t\t\tif Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT):\n\t\t\t\t# try to actuate joystick at the given position\n\t\t\t\t# print(\"try_actuate\", pos, event.position)\n\t\t\t\t_try_actuate(pos)\n\t\t\t\tif _is_over_button(pos):\n\t\t\t\t\tget_viewport().set_input_as_handled()\n\t\t\treturn\n\t\t\n\t\tif event.pressed and event.button_index == MOUSE_BUTTON_LEFT:\n\t\t\t# try to actuate button at the given position\n\t\t\t_try_actuate(pos)\n\t\t\t\n\t\t\t# we handled this input if it was over the button, otherwise let it propagate\n\t\t\tif _is_over_button(pos):\n\t\t\t\t_mouse_down_consumed = true\n\t\t\t\tget_viewport().set_input_as_handled()\n\t\treturn\n\n\t# actuated\n\t# if the mouse leaves the button area while moving, release\n\t# this works differently than a UI button which would only release\n\t# on mouse up, but this is more like a physical button would behave.\n\tif event is InputEventMouseMotion:\n\t\t# check if the mouse is still over the button\n\t\tif not _is_over_button(pos):\n\t\t\t_release()\n\t\tget_viewport().set_input_as_handled()\n\t\treturn\n\t\t\n\t# mouse button released, release the button\n\tif not event.pressed and event.button_index == MOUSE_BUTTON_LEFT:\n\t\t_release()\n\t\t# only consume the event if we also consumed the press\n\t\t\n\t\tget_viewport().set_input_as_handled()\n\t\n\n## Handles input from touch devices.\nfunc _handle_touch_input(event:InputEvent) -> void:\n\t# update finger positions\n\tif event is InputEventScreenTouch:\n\t\tif event.pressed:\n\t\t\t_finger_positions[event.index] = event.position\n\t\telse:\n\t\t\t_finger_positions.erase(event.index)\n\telif event is InputEventScreenDrag:\n\t\t_finger_positions[event.index] = event.position\n\t\n\t# everything that happens over the button area is considered handled\n\tif _is_over_button(event.position):\n\t\tget_viewport().set_input_as_handled()\n\t\n\tif not _is_actuated:\n\t\t# if any finger is over the button area, try to actuate\n\t\tfor position:Vector2 in _finger_positions.values():\n\t\t\tvar pos := _screen_to_world(position)\n\t\t\t_try_actuate(pos)\n\t\treturn\n\t\n\t# actuated\n\t# if no finger is currently over the button area, release\n\tfor position:Vector2 in _finger_positions.values():\n\t\tvar pos := _screen_to_world(position)\n\t\tif _is_over_button(pos):\n\t\t\treturn  # still over the button, do nothing\n\t\n\t_release()\n\n## Returns whether the given world position is over the button area.\nfunc _is_over_button(world_position:Vector2) -> bool:\n\treturn global_position.distance_squared_to(world_position) <= button_radius * button_radius\n\n\n## Tries to actuate the button using the given world position.\nfunc _try_actuate(world_position: Vector2):\n\tif not _is_actuated and _is_over_button(world_position) :\n\t\t_is_actuated = true\n\t\t_report_input()\n\n## Releases the button.\nfunc _release():\n\tif _is_actuated:\n\t\t_is_actuated = false\n\t\t_report_input()\n\n## Reports the current input state to the input system.\nfunc _report_input():\n\tvar event := InputEventJoypadButton.new()\n\tevent.button_index = button_index\n\tevent.pressed = _is_actuated\n\tevent.device = _virtual_joy_id\n\tGUIDE.inject_input(event)\n\tif draw_debug:\n\t\tqueue_redraw()\n\tchanged.emit()\n\n## Draws the debug representation of the button.\nfunc _draw():\n\tif not draw_debug:\n\t\treturn\n\t## \n\tvar color := Color(0.9, 0.9, 0.9, 0.8) if _is_actuated else Color(0.5, 0.5, 0.5, 0.5)\n\tdraw_circle(Vector2.ZERO, button_radius, color)\n"
  },
  {
    "path": "addons/guide/ui/virtual_joy/virtual_button/guide_virtual_button.gd.uid",
    "content": "uid://mtww7c1tlwt7\n"
  },
  {
    "path": "addons/guide/ui/virtual_joy/virtual_button/guide_virtual_button.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bmlvcsmaunrel\"\npath=\"res://.godot/imported/guide_virtual_button.svg-6bd2d850676d11110201f34b927d6171.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/virtual_joy/virtual_button/guide_virtual_button.svg\"\ndest_files=[\"res://.godot/imported/guide_virtual_button.svg-6bd2d850676d11110201f34b927d6171.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/ui/virtual_joy/virtual_button/guide_virtual_button_renderer.gd",
    "content": "@tool\n@icon(\"guide_virtual_button_renderer.svg\")\n## Base class for virtual button renderers.\nclass_name GUIDEVirtualButtonRenderer\nextends Control\n\nvar _button: GUIDEVirtualButton = null\nvar _was_actuated:bool = false\n\n## Returns whether or not the button is currently actuated.\nvar is_button_actuated:bool:\n\tget:\n\t\tif _button != null:\n\t\t\treturn _button._is_actuated\n\t\treturn false\t\t\n\t\t\n## Returns the button radius.\nvar button_radius:float:\n\tget:\n\t\tif _button != null:\n\t\t\treturn _button.button_radius\n\t\treturn 0.0\n\nfunc _notification(what:int) -> void:\n\tif what == NOTIFICATION_ENTER_TREE:\n\t\t\n\t\t_button = get_parent() as GUIDEVirtualButton\n\t\tif _button == null:\n\t\t\tpush_error(\"Button renderer must be a child of GUIDEVirtualButton. Button renderer will not work.\")\n\t\t\treturn\n\t\t_button.configuration_changed.connect(_on_configuration_changed)\n\t\tif Engine.is_editor_hint():\n\t\t\treturn\n\t\t# we only react to input changes in the real game, not in the editor.\n\t\t_button.changed.connect(func(): _update(_button._is_actuated))\n\n## Called when configuration changed on the parent button.\nfunc _on_configuration_changed() -> void:\n\tpass\n\t\n## Function to be implemented by subclasses to update the button visuals.\n## @param is_actuated: Whether or not the button is currently actuated.\nfunc _update(is_actuated:bool) -> void:\n\tpass\n\n\nfunc _get_configuration_warnings() -> PackedStringArray:\n\tvar results: PackedStringArray = []\n\n\tif (get_parent() as GUIDEVirtualButton) == null:\n\t\tresults.append(\"Button renderer must be a child of GUIDEVirtualButton\")\n\n\treturn results\n\n"
  },
  {
    "path": "addons/guide/ui/virtual_joy/virtual_button/guide_virtual_button_renderer.gd.uid",
    "content": "uid://d3jxhntwb8mhi\n"
  },
  {
    "path": "addons/guide/ui/virtual_joy/virtual_button/guide_virtual_button_renderer.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://clyr3m1jc0opf\"\npath=\"res://.godot/imported/guide_virtual_button_renderer.svg-799e4af664e5db9d3fac9eab9a070c20.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/virtual_joy/virtual_button/guide_virtual_button_renderer.svg\"\ndest_files=[\"res://.godot/imported/guide_virtual_button_renderer.svg-799e4af664e5db9d3fac9eab9a070c20.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/ui/virtual_joy/virtual_button/texture_renderer/actuated.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bhpkiauj6rd30\"\npath=\"res://.godot/imported/actuated.svg-437a9e6bbd2caf52c9ecba58b2821e8d.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/virtual_joy/virtual_button/texture_renderer/actuated.svg\"\ndest_files=[\"res://.godot/imported/actuated.svg-437a9e6bbd2caf52c9ecba58b2821e8d.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=false\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/ui/virtual_joy/virtual_button/texture_renderer/guide_virtual_button_texture_renderer.gd",
    "content": "@tool\n## Renders a virtual button using two textures for idle and actuated states.\n## The textures are uniformly scaled so they fully cover the button area\n## defined by the parent's `button_radius`.\nclass_name GUIDEVirtualButtonTextureRenderer\nextends GUIDEVirtualButtonRenderer\n\n## Texture shown when the button is NOT actuated.\n@export var idle_texture: Texture2D = preload(\"idle.svg\"):\n\tset(value):\n\t\tidle_texture = value\n\t\t_rebuild()\n\n## Texture shown when the button IS actuated.\n@export var actuated_texture: Texture2D = preload(\"actuated.svg\"):\n\tset(value):\n\t\tactuated_texture = value\n\t\t_rebuild()\n\nvar _idle_sprite: Sprite2D\nvar _actuated_sprite: Sprite2D\n\nfunc _ready() -> void:\n\t_rebuild()\n\n## React to configuration changes from the parent button.\nfunc _on_configuration_changed() -> void:\n\t_rebuild()\n\n## (Re)build child sprites and apply textures.\nfunc _rebuild() -> void:\n\t_idle_sprite = _ensure_sprite(_idle_sprite, idle_texture)\n\t_actuated_sprite = _ensure_sprite(_actuated_sprite, actuated_texture)\n\t_update(is_button_actuated)\n\n## Update the visibility based on actuation.\n## @param is_actuated Whether or not the button is currently actuated.\nfunc _update(is_actuated: bool) -> void:\n\t_idle_sprite.visible = not is_actuated\n\t_actuated_sprite.visible = is_actuated\n\n## Ensure a sprite exists, has the given texture, and is scaled to cover the\n## circular button area based on the current `button_radius`.\n## @param sprite The existing sprite (may be null/invalid).\n## @param texture The texture to assign to the sprite.\n## @return the existing sprite instance or a new one if it was invalid.\nfunc _ensure_sprite(sprite: Sprite2D, texture: Texture2D) -> Sprite2D:\n\tif not is_instance_valid(sprite):\n\t\tsprite = Sprite2D.new()\n\t\tsprite.centered = true\n\t\tadd_child(sprite)\n\t\n\tsprite.texture = texture\n\t\n\t# Apply uniform scaling so the sprite fully covers the circular button area.\n\tvar diameter: float = button_radius * 2.0\n\tif diameter > 0.0:\n\t\tvar tex: Texture2D = sprite.texture\n\t\tif is_instance_valid(tex):\n\t\t\tvar tex_size: Vector2 = tex.get_size()\n\t\t\tif tex_size.x > 0.0 and tex_size.y > 0.0:\n\t\t\t\tvar shortest: float = min(tex_size.x, tex_size.y)\n\t\t\t\tvar factor: float = diameter / shortest\n\t\t\t\tsprite.scale = Vector2(factor, factor)\n\t\t\t\tsprite.position = Vector2.ZERO\n\t\n\treturn sprite\n\n"
  },
  {
    "path": "addons/guide/ui/virtual_joy/virtual_button/texture_renderer/guide_virtual_button_texture_renderer.gd.uid",
    "content": "uid://cnsqwchlb1blg\n"
  },
  {
    "path": "addons/guide/ui/virtual_joy/virtual_button/texture_renderer/idle.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://sle777io03q\"\npath=\"res://.godot/imported/idle.svg-e63206fd6ee8f8608bcc0c5937890380.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/virtual_joy/virtual_button/texture_renderer/idle.svg\"\ndest_files=[\"res://.godot/imported/idle.svg-e63206fd6ee8f8608bcc0c5937890380.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=false\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/ui/virtual_joy/virtual_stick/guide_virtual_stick.gd",
    "content": "@tool\n@icon(\"guide_virtual_stick.svg\")\n## A virtual joystick.\nclass_name GUIDEVirtualStick\nextends GUIDEVirtualJoyBase\n\n## Positioning mode for the virtual stick.\nenum PositionMode {\n\t## The stick center stays at the designed position\n\tFIXED,\n\t\n\t## The stick center moves to touched/clicked position when the \n\t## stick is first actuated, then returns to the designed position\n\t## when released.\n\tRELATIVE\n}\n\n## Which stick this is.\nenum StickPosition {\n\t## This represents the left stick.\n\tLEFT,\n\t## This represents the right stick.\n\tRIGHT\n}\n\n## Radius of the zone in which interactions should be tracked.\n@export var interaction_zone_radius: float = 200:\n\tset(value):\n\t\tinteraction_zone_radius = value\n\t\tqueue_redraw()\n\t\tconfiguration_changed.emit()\n\n## Radius of the stick.\n@export var stick_radius: float = 100:\n\tset(value):\n\t\tstick_radius = value\n\t\tqueue_redraw()\n\t\tconfiguration_changed.emit()\n\n## Maximum radius that the stick can be moved.\n@export var max_actuation_radius: float = 100:\n\tset(value):\n\t\tmax_actuation_radius = value\n\t\tqueue_redraw()\n\t\tconfiguration_changed.emit()\n\n## Whether this is left or right stick (used for mapping the inputs).\n@export var stick_position: StickPosition = StickPosition.LEFT:\n\tset(value):\n\t\tstick_position = value\n\t\tconfiguration_changed.emit()\n\n## The position mode to use.\n@export var position_mode: PositionMode = PositionMode.FIXED:\n\tset(value):\n\t\tposition_mode = value\n\t\tconfiguration_changed.emit()\n\n# The initial position of the stick, when not being actuated.\nvar _initial_pos:Vector2\n\n# The global position where the stick was when it started to be actuated.\n# If the mode is FIXED, this is equal to _initial_pos, otherwise this\n# is the position where the finger came down within the interaction zone.\nvar _start_pos: Vector2\n\n# The current global position of the stick\nvar _current_pos: Vector2\n\n\n## The index of the finger which is currently controlling this stick. Is -1 if no finger controls it.\nvar _finger_index:int = -1\n\n## Stick position relative to the center of the stick in world coordinates.\nvar stick_relative_position:Vector2:\n\tget: return _current_pos - _start_pos\n\nfunc _ready() -> void:\n\tuse_top_left = true\n\tsize = Vector2.ZERO\n\t# the editor likes to set the size to 40x40, we'll override that\n\tif Engine.is_editor_hint():\n\t\tset_deferred(\"size\", Vector2.ZERO)\n\t_initial_pos = global_position\n\t_start_pos = global_position\n\t_current_pos = global_position\n\tmouse_filter = Control.MOUSE_FILTER_IGNORE\n\t# let renderers know the initial configuration\n\tconfiguration_changed.emit()\n\t# and the initial state\n\tchanged.emit()\n\n\tif Engine.is_editor_hint():\n\t\t# no input processing in the editor\n\t\treturn\n\t\t\n\t# connect to GUIDE\n\t_reconnect()\n\t# and report the initial state.\n\t_report_input()\n\n\nfunc _handle_mouse_input(event: InputEventMouse) -> void:\n\tif not _is_actuated:\n\t\t# if the mouse moves while we're not actuated, this has no effect\n\t\t# so we can skip this one.\n\t\tif event is InputEventMouseMotion:\n\t\t\treturn\n\n\t\tif event.pressed and event.button_index == MOUSE_BUTTON_LEFT:\n\t\t\tvar pos := _screen_to_world(event.position)\n\t\t\t# try to actuate joystick at the given position\n\t\t\t_try_actuate(pos)\n\t\t\tif _is_actuated:\n\t\t\t\tget_viewport().set_input_as_handled()\n\n\t\treturn\n\n\t# actuated\n\tif event is InputEventMouseMotion:\n\t\tvar pos := _screen_to_world(event.position)\n\t\t_move_towards(pos)\n\t\tget_viewport().set_input_as_handled()\n\t\treturn\n\n\tif not event.pressed and event.button_index == MOUSE_BUTTON_LEFT:\n\t\t_release()\n\t\tget_viewport().set_input_as_handled()\n\n\nfunc _handle_touch_input(event: InputEvent) -> void:\n\tif not _is_actuated:\n\t\t# ignore drag events if we're not actuated\n\t\tif event is InputEventScreenDrag:\n\t\t\treturn\n\n\t\t# must be an InputEventScreenTouch now\n\t\tif event.pressed:\n\t\t\tvar pos := _screen_to_world(event.position)\n\t\t\t_try_actuate(pos)\n\t\t\tif _is_actuated:\n\t\t\t\t_finger_index = event.index\n\t\t\t\tget_viewport().set_input_as_handled()\n\t\t\treturn\n\t\treturn\n\n\t# actuated\n\tif event.index != _finger_index:\n\t\treturn # we only react to events of the finger that actuated this stick\n\t\t\n\n\tif event is InputEventScreenDrag:\n\t\tvar pos := _screen_to_world(event.position)\n\t\t_move_towards(pos)\n\t\tget_viewport().set_input_as_handled()\n\t\treturn\n\n\t# must be an InputEventScreenTouch now\n\tif not event.pressed:\n\t\t_release()\n\t\tget_viewport().set_input_as_handled()\n\t\t_finger_index = -1\n\t\t\n\nfunc _try_actuate(world_position: Vector2) -> void:\n\tmatch position_mode:\n\t\tPositionMode.FIXED:\n\t\t\t# in fixed mode, the starting position must be inside the joystick area\n\t\t\tif world_position.distance_to(global_position) > stick_radius:\n\t\t\t\treturn\n\n\t\t\t_start_pos = global_position\n\t\t\t_current_pos = world_position\n\t\t\t_is_actuated = true\n\t\t\t_report_input()\n\n\t\tPositionMode.RELATIVE:\n\t\t\t# in relative mode, the starting position must be somewhere inside the \n\t\t\t# interaction area\n\t\t\tif world_position.distance_to(global_position) > interaction_zone_radius:\n\t\t\t\treturn\n\n\t\t\tglobal_position = world_position\n\t\t\t_start_pos = world_position\n\t\t\t_current_pos = world_position\n\t\t\t_is_actuated = true\n\t\t\t_report_input()\n\n## Moves the joystick towards the given world position.\nfunc _move_towards(world_position: Vector2) -> void:\n\tvar direction:Vector2 = _start_pos.direction_to(world_position)\n\tvar distance:float = _start_pos.distance_to(world_position)\n\t_current_pos = _start_pos + direction * min(distance, max_actuation_radius)\n\t_report_input()\n\n## Releases the joystick.\nfunc _release() -> void:\n\t_is_actuated = false\n\t# return to initial position\n\tglobal_position = _initial_pos\n\t\n\t_start_pos = global_position\n\t_current_pos = global_position\n\t_report_input()\n\n## Reports the current input state to the input system.\nfunc _report_input() -> void:\n\tvar direction := Vector2.ZERO\n\tif not _start_pos.is_equal_approx(_current_pos):\n\t\tdirection = _start_pos.direction_to(_current_pos)\n\n\tvar distance: float = _start_pos.distance_to(_current_pos)\n\tvar offset:Vector2 = direction * (distance / max_actuation_radius)\n\n\tmatch stick_position:\n\t\tStickPosition.LEFT:\n\t\t\t_send_event(JOY_AXIS_LEFT_X, offset.x)\n\t\t\t_send_event(JOY_AXIS_LEFT_Y, offset.y)\n\t\tStickPosition.RIGHT:\n\t\t\t_send_event(JOY_AXIS_RIGHT_X, offset.x)\n\t\t\t_send_event(JOY_AXIS_RIGHT_Y, offset.y)\n\tif draw_debug:\n\t\tqueue_redraw()\n\t\n\tchanged.emit()\n\t\n## Helper function to send input events to the input system.\nfunc _send_event(axis:int, value:float):\n\tvar event := InputEventJoypadMotion.new()\t\n\tevent.axis = axis\n\tevent.axis_value = value\n\tevent.device = _virtual_joy_id\n\tGUIDE.inject_input(event)\n\t\n\n## Draws the virtual stick and its interaction zone.\nfunc _draw() -> void:\n\tif not draw_debug:\n\t\treturn\n\t\n\tdraw_circle(Vector2.ZERO, interaction_zone_radius, Color(0.5, 0.5, 1.0, 0.5))\n\tdraw_circle(Vector2.ZERO, max_actuation_radius, Color(0.9, 0.2, 0.2, 0.5))\t\n\tdraw_circle(Vector2.ZERO, stick_radius, Color(0.9, 0.9, 0.3, 0.5))\n\n\tif _is_actuated:\n\t\tdraw_circle(stick_relative_position, stick_radius, Color(0.9, 0.9, 0.7, 0.5))\n\t\t\nfunc _get_configuration_warnings() -> PackedStringArray:\n\tvar result:PackedStringArray = []\n\tif get_parent() is Container:\n\t\tresult.append(\"Virtual sticks must be top level elements. They cannot be a child of a container.\")\n\treturn result\n"
  },
  {
    "path": "addons/guide/ui/virtual_joy/virtual_stick/guide_virtual_stick.gd.uid",
    "content": "uid://nrxpuvak5oc6\n"
  },
  {
    "path": "addons/guide/ui/virtual_joy/virtual_stick/guide_virtual_stick.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://dawv40l7btfsq\"\npath=\"res://.godot/imported/guide_virtual_stick.svg-14707457ab43649294450c78bb02e7c9.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/virtual_joy/virtual_stick/guide_virtual_stick.svg\"\ndest_files=[\"res://.godot/imported/guide_virtual_stick.svg-14707457ab43649294450c78bb02e7c9.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/ui/virtual_joy/virtual_stick/guide_virtual_stick_renderer.gd",
    "content": "@tool\n@icon(\"guide_virtual_stick_renderer.svg\")\n## Base class for virtual stick renderers.\nclass_name GUIDEVirtualStickRenderer\nextends Control\n\n\nvar _stick: GUIDEVirtualStick\nvar _was_actuated:bool = false\n\n## Returns the stick radius from virtual stick.\nvar stick_radius: float:\n\tget:\n\t\tif _stick != null:\n\t\t\treturn _stick.stick_radius\n\t\treturn 0.0\n\n## Returns the max actuation radius from the virtual stick.\nvar max_actuation_radius: float:\n\tget:\n\t\tif _stick != null:\n\t\t\treturn _stick.max_actuation_radius\n\t\treturn 0.0\n\n## Returns the interaction zone radius from the virtual stick.\nvar interaction_zone_radius: float:\n\tget:\n\t\tif _stick != null:\n\t\t\treturn _stick.interaction_zone_radius\n\t\treturn 0.0\n\t\t\n## Returns the position of the virtual stick relative to the center.\t\nvar stick_position: Vector2:\n\tget:\n\t\tif _stick != null:\n\t\t\treturn _stick.stick_relative_position\n\t\treturn Vector2.ZERO\n\n## Returns the starting position of the virtual stick in global coordinates.\nvar stick_start_position:Vector2:\n\tget:\n\t\tif _stick != null:\n\t\t\treturn _stick._start_pos\n\t\treturn Vector2.ZERO\n\t\t\t\n\t\t\n## Returns whether or not the stick is currently actuated.\nvar is_stick_actuated:bool:\n\tget:\n\t\tif _stick != null:\n\t\t\treturn _stick._is_actuated\n\t\treturn false\t\t\n\t\t\n\nfunc _notification(what:int) -> void:\n\tif what == NOTIFICATION_ENTER_TREE:\n\t\t_stick = get_parent() as GUIDEVirtualStick\n\t\tif _stick == null:\n\t\t\tpush_error(\"Stick renderer must be a child of GUIDEVirtualStick. Stick renderer will not work.\")\n\t\t\treturn\n\t\t_stick.configuration_changed.connect(_on_configuration_changed)\n\n\t\tif Engine.is_editor_hint():\n\t\t\treturn\n\t\t# we only react to input changes in the real game, not in the editor.\n\t\t_stick.changed.connect(_on_stick_changed)\n\n\n## Called when configuration changed on the parent stick (e.g., radii or modes changed).\nfunc _on_configuration_changed() -> void:\n\tpass\n\t\n\t\nfunc _on_stick_changed() -> void:\n\tvar direction: Vector2 = Vector2.ZERO\n\t# get the stick direction (if the stick is not in the start position)\n\tif not _stick._start_pos.is_equal_approx(_stick._current_pos):\n\t\tdirection = _stick._start_pos.direction_to(_stick._current_pos)\n\t\t\n\t# get the distance from the start position to the current position to \n\t# calculate the offset (normalized by the max actuation radius)\n\tvar distance: float = _stick._start_pos.distance_to(_stick._current_pos)\n\tvar offset: Vector2 = direction * (distance / _stick.max_actuation_radius)\n\t\n\t# track actuation state changes\n\tif _stick._is_actuated and not _was_actuated:\n\t\t_was_actuated = true\n\t\t\n\tif not _stick._is_actuated and _was_actuated:\n\t\t_was_actuated = false\n\t\t\n\t# Update the visuals based on the new state.\n\t_update(_stick._current_pos - global_position, offset, _stick._is_actuated)\n\t\n## Function to be implemented by subclasses to update the stick visuals.\n## @param joy_position The position relative to the position of this renderer.\n## @param joy_offset The normalized joy offset from the center of the stick, \n## taking into account the max actuation radius.\nfunc _update(joy_position: Vector2, joy_offset:Vector2, is_actuated:bool) -> void:\n\tpass\n\n\n\nfunc _get_configuration_warnings() -> PackedStringArray:\n\tvar results: PackedStringArray = []\n\n\tif (get_parent() as GUIDEVirtualStick) == null:\n\t\tresults.append(\"Stick renderer must be a child of GUIDEVirtualStick\")\n\n\treturn results\n\n"
  },
  {
    "path": "addons/guide/ui/virtual_joy/virtual_stick/guide_virtual_stick_renderer.gd.uid",
    "content": "uid://wvl4ybatr4uf\n"
  },
  {
    "path": "addons/guide/ui/virtual_joy/virtual_stick/guide_virtual_stick_renderer.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://c80jtic1dk2e2\"\npath=\"res://.godot/imported/guide_virtual_stick_renderer.svg-404295ac82685ed23dc21b8a7651216d.ctex\"\nmetadata={\n\"has_editor_variant\": true,\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/virtual_joy/virtual_stick/guide_virtual_stick_renderer.svg\"\ndest_files=[\"res://.godot/imported/guide_virtual_stick_renderer.svg-404295ac82685ed23dc21b8a7651216d.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=true\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/ui/virtual_joy/virtual_stick/texture_renderer/guide_virtual_stick_texture_renderer.gd",
    "content": "@tool\n## Renders a virtual stick using textures for outline, stick, and an optional hidden state.\n## Textures are uniformly scaled to match the current `stick_radius` so visuals\n## always correspond to the stick size.\nclass_name GUIDEVirtualStickTextureRenderer\nextends GUIDEVirtualStickRenderer\n\n## When to show the joystick.\nenum ShowMode {\n\t## Always show the joystick.\n\tALWAYS = 0,\n\t## Only show the joystick when actuated.\n\tON_ACTUATE = 1\n}\n\n## When the sticks should be shown on screen.\n@export var show_stick: ShowMode = ShowMode.ON_ACTUATE:\n\tset(value):\n\t\tshow_stick = value\n\t\t_rebuild()\n\n## The outline texture to use for the stick.\n@export var outline_texture: Texture2D = preload(\"stick_outline.svg\"):\n\tset(value):\n\t\toutline_texture = value\n\t\t_rebuild()\n\t\t\n## The stick texture to use for the stick.\t\t\n@export var stick_texture: Texture2D = preload(\"stick.svg\"):\n\tset(value):\n\t\tstick_texture = value\n\t\t_rebuild()\n\t\t\n## The texture to show when the stick is hidden (optional).\t\t\n@export var hidden_texture: Texture2D = preload(\"stick_hidden.svg\"):\n\tset(value):\n\t\thidden_texture = value\n\t\t_rebuild()\n\nvar _outline_sprite: Sprite2D\nvar _stick_sprite: Sprite2D\nvar _hidden_sprite: Sprite2D\n\nfunc _ready() -> void:\n\t_rebuild()\n\n## React to configuration changes on the parent stick (e.g., radius changes).\nfunc _on_configuration_changed() -> void:\n\t_rebuild()\n\n\t\n## (Re)build sprites and apply textures and scaling.\nfunc _rebuild() -> void:\n\t_outline_sprite = _ensure_sprite(_outline_sprite, outline_texture)\n\t_stick_sprite = _ensure_sprite(_stick_sprite, stick_texture)\n\t_hidden_sprite = _ensure_sprite(_hidden_sprite, hidden_texture)\n\t_update(Vector2.ZERO, Vector2.ZERO, is_stick_actuated)\n\t\n\t\n## Ensure a sprite exists, has the given texture, and is scaled to cover the\n## circular stick area based on the current `stick_radius`.\n## @param sprite The existing sprite (may be null/invalid).\n## @param texture The texture to assign to the sprite.\n## @return The ensured sprite instance.\nfunc _ensure_sprite(sprite: Sprite2D, texture: Texture2D) -> Sprite2D:\n\tvar ensured: Sprite2D = sprite\n\tif not is_instance_valid(ensured):\n\t\tensured = Sprite2D.new()\n\t\tensured.centered = true\n\t\tadd_child(ensured)\n\t\n\tensured.texture = texture\n\t\n\t# Scale uniformly so the sprite covers the stick circle based on stick_radius.\n\tvar diameter: float = stick_radius * 2.0\n\tif diameter > 0.0:\n\t\tvar tex: Texture2D = ensured.texture\n\t\tif is_instance_valid(tex):\n\t\t\tvar tex_size: Vector2 = tex.get_size()\n\t\t\tif tex_size.x > 0.0 and tex_size.y > 0.0:\n\t\t\t\tvar shortest: float = min(tex_size.x, tex_size.y)\n\t\t\t\tvar factor: float = diameter / shortest\n\t\t\t\tensured.scale = Vector2(factor, factor)\n\t\t\t\tensured.position = Vector2.ZERO\n\treturn ensured\n\t\t\n\t\t\n\t\n## Update the visuals based on the new state.\n## @param joy_position The position relative to this renderer.\n## @param joy_offset Normalized offset from center using max actuation radius.\n## @param is_actuated Whether or not the stick is currently actuated.\nfunc _update(joy_position: Vector2, joy_offset: Vector2, is_actuated: bool) -> void:\n\t_stick_sprite.position = joy_position\n\t\n\tvar should_be_visible: bool = is_actuated or show_stick == ShowMode.ALWAYS \n\t\n\t_stick_sprite.visible = should_be_visible\n\t_outline_sprite.visible = should_be_visible\n\t_hidden_sprite.visible = not should_be_visible\n\t\t\n\t\t\n"
  },
  {
    "path": "addons/guide/ui/virtual_joy/virtual_stick/texture_renderer/guide_virtual_stick_texture_renderer.gd.uid",
    "content": "uid://d3furq83klcts\n"
  },
  {
    "path": "addons/guide/ui/virtual_joy/virtual_stick/texture_renderer/stick.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cc4fmyqmn643v\"\npath=\"res://.godot/imported/stick.svg-94b9c8a0a27d1ad7d37a84348276b39b.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/virtual_joy/virtual_stick/texture_renderer/stick.svg\"\ndest_files=[\"res://.godot/imported/stick.svg-94b9c8a0a27d1ad7d37a84348276b39b.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=false\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/ui/virtual_joy/virtual_stick/texture_renderer/stick_hidden.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://dld1wgl4vgrwq\"\npath=\"res://.godot/imported/stick_hidden.svg-b75d613e0331709c76e919bca5336b5d.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/virtual_joy/virtual_stick/texture_renderer/stick_hidden.svg\"\ndest_files=[\"res://.godot/imported/stick_hidden.svg-b75d613e0331709c76e919bca5336b5d.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=false\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "addons/guide/ui/virtual_joy/virtual_stick/texture_renderer/stick_outline.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cj30wnqfj0wdg\"\npath=\"res://.godot/imported/stick_outline.svg-46a87c838245d7c9c70a7057288b11f0.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://addons/guide/ui/virtual_joy/virtual_stick/texture_renderer/stick_outline.svg\"\ndest_files=[\"res://.godot/imported/stick_outline.svg-46a87c838245d7c9c70a7057288b11f0.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=false\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "asset_library/.gdignore",
    "content": ""
  },
  {
    "path": "docs/.gdignore",
    "content": ""
  },
  {
    "path": "docs/.gitignore",
    "content": "vendor\n_site\nGemfile.lock\n.jekyll-cache\n"
  },
  {
    "path": "docs/Gemfile",
    "content": "source \"https://rubygems.org\"\nruby RUBY_VERSION\n\n# Hello! This is where you manage which Jekyll version is used to run.\n# When you want to use a different version, change it below, save the\n# file and run `bundle install`. Run Jekyll with `bundle exec`, like so:\n#\n#     bundle exec jekyll serve\n#\n# This will help ensure the proper Jekyll version is running.\n# Happy Jekylling!\n# gem \"jekyll\", \"~> 4.2.2\"\n\n# This is the default theme for new Jekyll sites. You may change this to anything you like.\n# gem \"minima\"\n\n# If you want to use GitHub Pages, remove the \"gem \"jekyll\"\" above and\n# uncomment the line below. To upgrade, run `bundle update github-pages`.\ngem \"github-pages\", group: :jekyll_plugins\n\n# If you have any plugins, put them here!\n# group :jekyll_plugins do\n#   gem \"jekyll-github-metadata\", \"~> 1.0\"\n# end\n\ngem 'wdm', '>= 0.1.1' \ngem \"webrick\", \"~> 1.8\"\n"
  },
  {
    "path": "docs/_config.yml",
    "content": "# Welcome to Jekyll!\n#\n# This config file is meant for settings that affect your whole blog, values\n# which you are expected to set up once and rarely edit after that. If you find\n# yourself editing these this file very often, consider using Jekyll's data files\n# feature for the data you need to update frequently.\n#\n# For technical reasons, this file is *NOT* reloaded automatically when you use\n# 'jekyll serve'. If you change this file, please restart the server process.\n\n# Site settings\n# These are used to personalize your new site. If you look in the HTML files,\n# you will see them accessed via {{ site.title }}, {{ site.email }}, and so on.\n# You can create any custom variable you would like, and they will be accessible\n# in the templates via {{ site.myvariable }}.\n\ntitle: G.U.I.D.E - Godot Unified Input Detection Engine\nemail: info@godotneers.com\nauthor: Jan Thomä\ndescription: > # this means to ignore newlines until \"baseurl:\"\n  A simple but powerful way to handle input in the Godot Engine.\n\nbaseurl: \"/G.U.I.D.E\" # the subpath of your site, e.g. /blog\n\n# This is mostly for testing\nurl: \"https://godotneers.github.io/\" # the base hostname & protocol for your site\n\n# Issue Tracker\nissue_tracker: \"https://github.com/godotneers/G.U.I.D.E/issues\"\n\n# Social (First three Required)\nrepo: \"https://github.com/godotneers/G.U.I.D.E\"\ngithub_user: \"godotneers\"\ngithub_repo: \"G.U.I.D.E\"\ngithub_branch: \"main\"\n\n# Optional\n\n# Should there be feedback buttons at the bottom of pages?\nfeedback: false\n\n# Link to a privacy policy in footer, uncomment and define if wanted\n# privacy: https://domain.com/privacy\n\n# google-analytics: UA-XXXXXXXXXX\n# Image and (square) dimension for logo (don't start with /)\n# If commented, will use material hat theme\n# logo: \"assets/img/logo/SRCC-square-red.png\"\nlogo_pixels: 34\ncolor: \"#30638e\"\n# color: \"#8c1515\" # primary color for header, buttons\n\n# Build settings\nmarkdown: kramdown\n\n# If you add tags to pages, define this variable to link them to some external search\n# If you want to link to tags locally on the site, leave this commented out\n# tag_search_endpoint: https://ask.cyberinfrastructure.org/search?q=\ntag_color: primary # danger, success, warning, primary, info, secondary\n\naccentColor: red # purple, green, etc.\nthemeColor: red # purple, green, blue, orange, purple, grey\nfixedNav: 'true' # true or false\n\npermalink: /:year/:title/\nexclude: [ _site, CHANGELOG.md, LICENSE, README.md, vendor ]\n\n# Collections\ncollections:\n  docs:\n    output: true\n    permalink: /:path\n  \n  recipes:\n    output: true\n    permalink: /:path\n\n# Defaults\ndefaults:\n  - scope:\n      path: \"_docs\"\n      type: \"docs\"\n    values:\n      layout: page\n  - scope:\n      path: \"\"\n      type: \"pages\"\n    values:\n      layout: \"page\"\n  - scope:\n      path: \"posts\"\n      type: \"posts\"\n    values:\n      layout: \"post\"\n\n"
  },
  {
    "path": "docs/_data/toc.yml",
    "content": "- title: \"Home\"\n  url: \"\"\n- title: \"Installation & Updating\"\n  url: \"installation\"\n- title: \"Quick Start\"\n  url: \"quick-start\"\n- title: \"Video Tutorials\"\n  url: \"video-tutorials\"\n- title: \"Usage\"\n  url: \"usage/concepts\"\n  links:\n    - title: \"How G.U.I.D.E works\"\n      url: \"usage/concepts\"\n    - title: \"Configuration and input handling\"\n      url: \"usage/configuration-and-input-handling\"\n    - title: \"Input prompts\"\n      url: \"usage/input-prompts\"\n    - title: \"Virtual joysticks\"\n      url: \"usage/virtual-joysticks\"\n    - title: \"Remapping input\"\n      url: \"usage/remapping-input\"\n    - title: \"Extending G.U.I.D.E\"\n      url: \"usage/extending-guide\"\n    - title: \"Recipes\"\n      url: \"usage/recipes\"\n- title: \"Reference\"\n  url: \"reference/modifiers\"\n  links:\n    - title: \"Examples\"\n      url: \"reference/examples\"\n    - title: \"Input reference\"\n      url: \"reference/inputs\"\n    - title: \"Modifier reference\"\n      url: \"reference/modifiers\"\n    - title: \"Trigger reference\"\n      url: \"reference/triggers\"\n- title: \"Updates & Support\"\n  url: \"updates-and-support\"\n  links:\n    - title: \"FAQ\"\n      url: \"faq\"\n"
  },
  {
    "path": "docs/_docs/01_installation.md",
    "content": "---\nlayout: page\ntitle: Installation and Updates\npermalink: /installation\ndescription: \"Here you can find the installation instructions for the plugin.\"\n---\n\n## Requirements\n\nThis plugin requires Godot 4.2.0 or later. Earlier versions of Godot 4 will not work because the plugin uses features that were introduced only in Godot 4.2.0. Godot 3 is not supported.\n\n## Installation process\n### Installation with the Godot Asset Library\n\nThe easiest way to install the plugin is to use the Godot Asset Library. Search for \"G.U.I.D.E\" and install the plugin. You can exclude the `guide_examples` folder if you don't need the examples. Be sure to follow the [important steps after installation](#important-steps-after-installation).\n\n### Installation with Godot Goodie Grabber (GGG)\n\nIf you use [Godot Goodie Grabber](https://godotneers.github.io/ggg) to manage your project's dependencies, you can add G.U.I.D.E directly from the asset library using its asset ID `3503`:\n\n```bash\nggg add asset --id 3503 --name \"guide\"\nggg sync\n```\n\nThis installs the plugin - including the examples - into your project's `addons` folder. If you don't want the examples, edit the resulting entry in `ggg.toml` to add an `exclude` section:\n\n```toml\n[[dependency]]\nname     = \"guide\"\nasset_id = 3503\nexclude = [\"guide_examples\"]\n```\n\nRun `ggg sync` again after editing `ggg.toml` to apply the change. Be sure to follow the [important steps after installation](#important-steps-after-installation).\n\n### Manual installation\n\nYou can also download a ZIP file of this repository and extract it, then copy the `addons/guide` folder into your project's `addons` folder.\n\n### Important steps after installation\nAfter you installed it, make sure you enable the plugin in the project settings:\n\n![Enabling the plugin in the project settings]({{ site.baseurl }}/assets/img/manual/installation_enable_plugin.png)\n\nAlso, please **restart the Godot editor** after enabling the plugin. This is required, because Godot usually doesn't fully pick up new plugins while it's running. If you don't restart the editor the plugin might not work correctly or not at all.\n\nThe following video shows the installation process in detail and launches one of the examples to verify that the plugin is working correctly.\n\n{% include video.html path=\"assets/img/manual/manual_installation.mp4\" %}\n\n\n## Updating from an earlier version\n\n### Before you update\n\nRegardless of how you installed the plugin, do these steps before updating:\n\n- **Be sure you have a backup of your project or have it under version control, so you can go back in case things don't work out as intended.**\n- Check the [CHANGES.md]({{site.repo}}/blob/main/CHANGES.md) for any breaking changes that might impact your project and any special update instructions.\n- Close Godot. It's important to not have the project opened while running the update.\n\n### Updating with Godot Goodie Grabber (GGG)\n\nRun the following commands in your project directory:\n\n```bash\nggg update\nggg sync\n```\n\n`ggg update` fetches the latest version from the asset library then installs the updated files into your project removing any stale files. You can now open the project again in Godot and continue working on it.\n\n### Updating a manual / asset lib installation\n\nThe asset library currently has no support for plugin updates. Therefore, you'll have to manually update the plugin. This also applies if you installed manually in the first place:\n\n- Download the version you want to install from the [Release List]({{site.repo}}/releases) (use the _Source Code ZIP_ link).\n- In your project locate the `guide` folder within the `addons` folder and delete the `guide` folder with all of its contents.\n- Unpack your downloaded ZIP file somewhere. Inside the unpacked ZIP file structure, locate the `guide` folder within the `addons` folder.\n- Move the `guide` folder you located in the previous step into the `addons` folder of your project.\n- The plugin is now updated. You can now open the project again in Godot and continue working on it.\n"
  },
  {
    "path": "docs/_docs/02_quick_start.md",
    "content": "---\nlayout: page\ntitle: Quick Start\npermalink: /quick-start\ndescription: \"Quickly set up G.U.I.D.E in your project.\"\n---\n\n## Introduction\n\nThis is a quick start guide to get you up and running with G.U.I.D.E. within a few minutes. We assume that you have a basic understanding of Godot, e.g.  how to use the editor and create basic stuff like scenes and sprites. If you are new to Godot, you probably don't want to use G.U.I.D.E just yet, and we recommend  to first [get acquainted with the engine](https://docs.godotengine.org/en/stable/getting_started/step_by_step/index.html).\n\n## Installation\n\nFirst, you need to install G.U.I.D.E. Check out the [installation instructions]({{site.baseurl}}/installation) on how to do that and then come back here.\n\n\n## What we will do\n\nIn this quick start guide we'll do a very simple example of controlling a player with the commong _WSAD_ keys. This will move the player up, down, left and right. We'll also add an action that makes the player say \"Hi\" which is triggered by the _Space_ key. You can find the finished version of this example in the `quick_start` folder of the `guide_examples` folder that comes with the plugin.\n\n## Setting up the scene\n\nWe'll need a scene where we have a player that can move around. So first let's create a new 2D scene. As a player we'll just use a simple sprite and we'll add a label in a panel container to display the \"Hi\" message.\n\n![Scene Setup]({{site.baseurl}}/assets/img/manual/quick_start_scene_setup.png)\n\nWe'll rename the panel container to `MessagePanel`, hide it by default and also assign it a scene unique name to make it easier to find in the script later. \n\n![Scene Setup #2]({{site.baseurl}}/assets/img/manual/quick_start_scene_setup_2.png)\n\n\n## Creating actions\n\nG.U.I.D.E is all about actions, so let's create the actions that we'll need. Our player should be able to do two things: Move around and say \"Hi\". So we'll create two actions: `move` and `say_hi`. G.U.I.D.E. actions are Godot resources, so we create them like any other resource in Godot - right-click in the file system dock and select  _Create New_ and then _Resource..._. Now a dialog will pop up and we can search for _GUIDEAction_ to create a new G.U.I.D.E action. We name this action `move.tres` and save it somewhere in our project. We can now duplicate this action with `Ctrl+D` and rename the duplicate to `say_hi.tres`.\n\n{% include video.html path=\"assets/img/manual/quick_start_create_actions.mp4\" %}\n\n### Setting up the actions\n\nActions in G.U.I.D.E have a few things that can be set up. The most important thing is the action's value type. This determines what kind of data we can receive from the action. Our `move` action will be a 2-dimensional axis because it contains horizontal and vertical movement. The `say_hi` action will be a simple button press, so its value type will be a boolean. We can set this up in the inspector of the action. So let's double-click on the `move.tres` action to open it in the inspector. There we have a property named _Action value type_ that we can set to _Axis 2d_:\n\n![Action value types]({{site.baseurl}}/assets/img/manual/quick_start_action_value_type.png)\n\n\nFor the `say_hi` action we don't need to change anything, as the default value type is already _Bool_. We also can ignore all the other settings right now because we don't need them for this example.\n\n## Binding the actions to input\n\nNow that we have our actions, we need to bind them to input events. In G.U.I.D.E this is done with a _Mapping Context_. A mapping context is a collection of bindings that map input events to actions. Like an action, a mapping context is a Godot resource, so we can create a new mapping context by right-clicking in the file system dock and selecting _Create New_ and then _Resource..._ and select _GUIDEMappingContext_ in the dialog that pops up. We name this mapping context `quickstart.tres`:\n\n![Create Mapping Context]({{site.baseurl}}/assets/img/manual/quick_start_create_mapping_context.png)\n\n\nNow we can double-click `quickstart.tres` to edit this mapping context. G.U.I.D.E provides a customized editor for setting up the mapping context, so instead of the usual inspector, the editor will open in the _G.U.I.D.E_ tab of the editor.\n\nWe now see an empty mapping context. We can add a new action mapping to it by clicking the _+_ button. This will add a new row to the mapping context where we can set up the mapping. First we need the action that we want to create a mapping for. We can just drag this into our mapping context from the file system dock. \n\n{% include video.html path=\"assets/img/manual/quick_start_create_action_mapping.mp4\" %}\n\nNow we can create input mappings for our action. Our action should be driven by four keys - _W_, _S_, _A_ and _D_. So let's add 4 input mappings to our action mapping by pressing the _+_ button in the _Input Mappings_ section. We get 4 rows, and in each we can click the edit button to bind input. A popup will open and ask us which kind of input we want to bind. We want to do a key press, which is a boolean input. So we select _Boolean_. Now the dialog tells us to actuate the input we want to use. We start with _W_ so we press the _W_ key. If we're happy, we press the _Accept_ key and the input will be added to our action mapping. We repeat this for the other keys.\n\n{% include video.html path=\"assets/img/manual/quick_start_bind_wasd_keys.mp4\" %}\n\n### Getting directional input\n\nSo far we only have bound the keys to the action, but right now we haven't set up how each key affects the action. We want the _W_ key to move the player up, the _S_ key to move the player down and the _A_ and _D_ keys to move the player left and right. With Godot's input system we would have created 4 actions for each direction and then have used `Input.get_vector(\"left\", \"right\", \"up\", \"down\")` to get the direction. In G.U.I.D.E we can set this up directly in the action mapping so our code doesn't need to care about this. \n\nAll input in G.U.I.D.E is represented as a `Vector3`. This is because G.U.I.D.E is designed to work with all kinds of input devices, and a `Vector3` can represent all of them. For our simple example we only need the `x` and `y` components of the `Vector3`.\n\nWhen we press a key on the keyboard, G.U.I.D.E will write a `1` into the `x` component of the input vector. When we release the key, G.U.I.D.E will write a `0` into the `x` component. So right now if we press any of the 4 keys, we would always get an input vector of `(1, 0, 0)`. We can now use modifiers to change how the input is processed. Let's start with the _W_ key. This should be the up key. In Godot the up direction is negative `y` so we need to negate that input value and move it into the `y` component. We can do this with modifiers. To negate the value, we can use the _Negate_ modifier and to move the input into another component, we use the _Input swizzle_ modifier. By default the _Input swizzle_ modifier will swap the `x` and `y` coordinates of our input so we don't need to do any additional configuration.\n\n{% include video.html path=\"assets/img/manual/quick_start_modifiers.mp4\" %}\n\nNow we can set up the _S_ key which is positive `y`, so for this we only need an _Input swizzle_ modifier. The _A_ key is negative `x`, so we add a _Negate_ modifier and the _D_ key is positive `x`, so we don't need any modifiers for this.\n\n{% include video.html path=\"assets/img/manual/quick_start_modifiers_2.mp4\" %}\n\nThat's it for the movement. Now we can set up the `say_hi` action. This is a simple button press, so we don't need any modifiers for this. We can just bind the _Space_ key to the action. This works the same way as binding the _WASD_ keys, so at the end our mapping context should look like this:\n\n![Mapping Context]({{site.baseurl}}/assets/img/manual/quick_start_mapping_context_with_hi.png)\n\n### Controlling the action with triggers\n\nNow our `say_hi` action works a little bit different than the move `action`. The move keys will be continuously pressed and as long as they are pressed the player will move. The `say_hi` action should only be triggered once when the _Space_ key is pressed but not repeatedly while the key is held down. So we need to add a custom trigger to our `say_hi` action. We can do this in the _Triggers_ section, and for this one we add a _Pressed_ trigger. This trigger will only trigger the action once when the input is pressed and then not trigger it again until the input is released and pressed again. This is similar to what Godot would do with the `Input.is_action_just_pressed` function.\n\n{% include video.html path=\"assets/img/manual/quick_start_pressed_trigger.mp4\" %}\n\n## Using the actions in code\n\nNow that we have set up the mapping context, we can actually start using these actions in our code. Let's first create a script for the player. We call it `player.gd` and attach it to the player node:\n\n```gdscript\nextends Sprite2D\n\n## The speed at which the player moves.\n@export var speed:float = 300\n## The action that moves the player.\n@export var move_action:GUIDEAction\n\nfunc _process(delta:float):\n    # Get the input value from the action and move the player.\n    position += move_action.value_axis_2d * speed * delta\n```\n\nWe see that we have a new property `move_action` that is of type `GUIDEAction`. In contrast to Godot's built-in input system, actions are resources, and we can inject them into our script like any other resource. This has a few benefits:\n- We don't use strings to refer to the action, so we can't make typos.\n- We can right-click on an action in the file system and can quickly find where it is used by selecting _View owners_.\n- We can easily use a different action on the existing script. For example if we have local multiplayer, we can use the same script to control two player instances, each using a different action for player 1 and 2.\n\nTo inject the action into the script, we can just drag the `move.tres` action from the file system dock into the `move_action` property in the inspector.\n\n![Injecting the action]({{site.baseurl}}/assets/img/manual/quick_start_player_setup.png)\n\n\nNow in the `_process` function we can get the value of the action by calling `move_action.value_axis_2d`. This will return a `Vector2` with the current input value of the action. This is automatically calculated by adding up all the input values that are bound to the action and applying the modifiers to them. So we don't need to care about how the input is processed, we just get the final value. This also has a few benefits:\n\n- We can easily bind this action to different input events. If we wanted to use the mouse or a joystick to move the player, we could just bind the mouse movement or a 2D joystick axis to the `move` action and the script wouldn't need to change.\n- Things like deadzones or input inversion (e.g. reverse up/down, left/right) can be set up in the mapping context and don't need to be handled in the script.\n\nIf we now start the game, nothing will happen though. This is because we haven't enabled the mapping context yet. The mapping context is usually not enabled in the player script because that is something that is done at the start of the game. So we'll also add a script to the root of our scene which we name `game.gd`:\n\n```gdscript\nextends Node2D\n\n## The mapping context that we use\n@export var mapping_context:GUIDEMappingContext\n\nfunc _ready(): \n    GUIDE.enable_mapping_context(mapping_context)\n```\n\nThis is a very simple script that just enables the mapping context using the `GUIDE` autoload that is automatically enabled once we install G.U.I.D.E into our project. Like with our action, the mapping context is a resource, so we make an `@export` for it. We can now drag the `quickstart.tres` mapping context into the `mapping_context` property in the inspector.\n\n![Injecting the mapping context]({{site.baseurl}}/assets/img/manual/quick_start_game_setup.png)\n\nNow if we start the game, we can move the player around with the _WASD_ keys:\n\n{% include video.html path=\"assets/img/manual/quick_start_move.mp4\" %}\n\nNow all that is left is to add the `say_hi` action to the player. We also inject this action into the player script and then drag it into the inspector:\n\n```gdscript\n## The action that says hi.\n@export var say_hi_action:GUIDEAction\n```\n\n![Injecting the say_hi action]({{site.baseurl}}/assets/img/manual/quick_start_player_setup_2.png)\n\nNow because the `say_hi` action is a button press we don't need to check it every frame in our `_process` function. Instead we can use the `triggered` signal that is emitted by the action when it is triggered. We can connect this signal to a function that will show the message panel when the action is triggered:\n\n\n```gdscript\nfunc _ready():\n    # Call the `say_hi` function whenever the say_hi_action is triggered.\n    say_hi_action.triggered.connect(_say_hi)\n\n\nfunc _say_hi():\n    # Quickly show and hide message panel\n    %MessagePanel.visible = true\n    await get_tree().create_timer(0.5).timeout\n    %MessagePanel.visible = false\n```\n\nNow if we start the game, we can move the player around with the _WASD_ keys and when we press the _Space_ key, the player will say \"Hi\":\n\n{% include video.html path=\"assets/img/manual/quick_start_final_result.mp4\" %}\n\nThat's it! You have now set up G.U.I.D.E in your project and created a simple example that uses actions to control a player. Check the `guide_examples` folder that comes with the plugin for more examples and ideas on how to use G.U.I.D.E in your project."
  },
  {
    "path": "docs/_docs/03_video_tutorials.md",
    "content": "---\nlayout: page\ntitle: Video tutorials\npermalink: /video-tutorials\ndescription: \"Video tutorials showing how to use G.U.I.D.E\"\n---\n\n## Introduction\n\nIf you prefer to learn in video form, check out the videos on this page which give your an introduction on how G.U.I.D.E works and how to use it in your game. Each video is about 1 hour and comes with an example project.\n\n### Part 1 - A G.U.I.D.E to input in Godot\n\nThis video uses a 3D tower defense game as an example to show the basics of G.U.I.D.E.\n\n<iframe width=\"560\" height=\"315\" src=\"https://www.youtube-nocookie.com/embed/gNiiaNViaUg?si=E95F5diRTzWuBtTi\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen></iframe>\n\n\n### Part 2 - Mixed input and input prompts\n\nThis video shows how to handle input from multiple different input devices like keyboard, mouse and controller simultaneously. It also shows to to display input prompts to the player, so the player knows which inputs trigger certain actions in the game.\n\n<iframe width=\"560\" height=\"315\" src=\"https://www.youtube-nocookie.com/embed/lEBYnQC-vJw?si=DoY6faH6Bqbp4wST\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen></iframe>\n\n\n### Part 3 - Remapping controls\n\nThis video shows how to use the built-in `Remapper` class to re-map controls while your game is running. It also covers handling conflicts and saving and loading your changed mappings.\n\n<iframe width=\"560\" height=\"315\" src=\"https://www.youtube-nocookie.com/embed/RCKqouD9bPI?si=G4YNB7SKcUGMokJK\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen></iframe>"
  },
  {
    "path": "docs/_docs/06_faq.md",
    "content": "---\nlayout: page\ntitle: Frequently Asked Questions\npermalink: /faq\ndescription: \"This page contains answers to frequently asked questions about G.U.I.D.E.\"\n---\n\n## The autoload fails to load after installing G.U.I.D.E from the AssetLib\n\nThis is a known issue ([#161]({{site.issue_tracker}}/161)) caused by a bug in certain versions of Godot. After installing G.U.I.D.E from the AssetLib, the `GUIDE` autoload may fail to load with an error like `Failed to create an autoload, can't load from UID or path`.\n\nThe workaround is to restart the Godot editor after the AssetLib installation completes, and only then enable the plugin in the project settings. This gives Godot a chance to properly register all files before the plugin is activated. If you have already enabled the plugin and are seeing the error, disable the plugin, restart the editor, and re-enable it.\n\nThis bug has been fixed in Godot's master branch and the fix will be included in a future stable release.\n\n## I'm getting an error message when trying to use G.U.I.D.E in my project\n\nIn general if this happens it is caused by one of the following reasons:\n\n- The plugin is not enabled in the project settings or the editor has not been restarted after enabling the plugin. Please make sure to follow the [installation instructions]({{site.baseurl}}/installation) carefully.\n- You are running G.U.I.D.E code in an autoload. If your autoload executes code that uses G.U.I.D.E before the `GUIDE` autoload is initialized, you will get an error. Make sure to check the autoload order in the project settings and make sure that the `GUIDE` autoload is loaded before any autoload that uses G.U.I.D.E.\n- You are using some plugins that might interfere with G.U.I.D.E. If you get strange errors, try disabling other plugins to see if they are causing the issue. If you find a plugin that causes issues, please [report it]({{site.issue_tracker}}) so I can investigate.\n\nIf none of these steps help, please [report the issue in the issue tracker]({{site.issue_tracker}}) and I will try to help you. Be sure to include as much information as possible about your setup and the error message you are getting.\n\n## The plugin doesn't work with the latest Godot preview version\n\nThis can happen because in general the aim of the plugin is to work with the latest stable version of Godot. Preview versions of Godot usually are not that stable and trying to make the plugin work with them can be a moving target. If you find an issue with a preview version of Godot, please [report it]({{site.issue_tracker}}), so I can investigate, but a fix might not materialize until the stable version is released.\n\n## Where can I ask questions about how to use G.U.I.D.E?\n\nYou can ask questions in the [GitHub issue tracker]({{site.issue_tracker}}). Questions about how to use the plugin are marked with the `Knowledge` label, which allows you to easily [find every question]({{site.issue_tracker}}?q=is%3Aissue%20label%3Aknowledge%20) that has been asked before.\n\n## Why don't you have a Discord server for support?\n\nI'm a solo developer and I don't have the bandwidth to monitor a Discord server and to answer questions in real-time. Also, I find having a Discord running all day to be very distracting which negatively affects my productivity. The GitHub issue tracker is currently the best way for me to keep track of questions and issues and to answer them in a timely manner."
  },
  {
    "path": "docs/_docs/07_support.md",
    "content": "---\nlayout: page\ntitle: Updates and support\npermalink: /updates-and-support\ndescription: \"Information about updates and support for G.U.I.D.E\"\n---\n\n## Introduction\n\nG.U.I.D.E is a somewhat complex piece of software that is maintained by me in my spare time. As such, there are some constraints on how updates and support will be handled.  \n\n## Updates\n\nI will release updates to G.U.I.D.E as they are completed. These updates may include new features, bug fixes, performance improvements, or compatibility updates with future versions of Godot. Newer versions of G.U.I.D.E may require newer versions of Godot, so make sure to check the release notes for compatibility information. In general, the plugin will be updated to work with the latest stable version of Godot. \n\nG.U.I.D.E may not always be compatible with the latest development previews and betas of Godot. If you find an issue with a development version of Godot, please report it, so I can investigate and fix it. That being said, a fix might not be available right away as especially with development versions of Godot, things can change rapidly and this week's fix might be made obsolete by next week's development version of Godot.\n\nAt some point, support for older versions of Godot might be dropped if necessary to keep the codebase maintainable or take advantage of new features. I will communicate this in the release notes. You can still use older versions of G.U.I.D.E with older versions of Godot, but you might miss out on new features or bug fixes. I don't have the bandwidth to support multiple versions of this addon for multiple Godot versions. So, in general, neither features nor bugfixes will be backported to older versions. In case of a critical bug that affects a large number of users, I might consider backporting a fix to an older version. Expect this to happen only in exceptional cases.\n\n## Support\n\nYou can report issues or ask for help in the [GitHub issue tracker]({{site.issue_tracker}}). Questions about how to use the plugin are marked with the `Knowledge` label, so you can [filter]({{site.issue_tracker}}?q=is%3Aissue%20label%3Aknowledge%20) for them to find answers for questions that others have asked already. "
  },
  {
    "path": "docs/_docs/reference/03_examples.md",
    "content": "---\nlayout: page\ntitle: Examples\npermalink: /reference/examples\ndescription: \"Examples that ship with G.U.I.D.E.\"\nno_toc: true\n---\n\n# {{ page.title }}\n\nG.U.I.D.E ships with quite a few small example projects that demonstrate how to use the plugin. You can find the examples in the `guide_examples` folder in the plugin's root directory. The following examples are included:\n\n- `2d_axis_mapping` - Shows how to map 2D input from WASD keys to a 2D action so it can move a 2D character.\n- `action_priority` - Shows the built-in action priority system. The example shows how to handle actions with overlapping input. In the example players have two spell quickbars bound to the controller D-pad. The first bar can be accessed by hitting the D-pad directly , the second bar can be accessed by holding the left trigger and hitting the D-pad. G.U.I.D.E will prioritize the second bar over the first bar if both are pressed at the same time.\n- `combine_contexts` - Shows how to combine multiple mapping contexts that map to the same actions. Both keyboard and controller inputs are enabled simultaneously, allowing seamless input from multiple sources without needing to switch contexts. See also `input_scheme_switching` which shows a different approach where you switch between input schemes instead of combining them.\n- `combos` - Shows the built-in combo trigger. Players can double-tap to perform a dash or use a sequence of inputs to perform a special fireball attack.\n- `hair_trigger` - Shows how to use a hair trigger to implement rapid fire. Provides a visualization of the hair trigger's current state.\n- `input_contexts` - Shows using multiple mapping contexts at the same time. In the example the player uses one set of action mappings to control a character and switches to another set of action mappings when controlling a boat. This allows to trigger different actions with the same input controls.\n- `input_scheme_switching` - Shows how to switch between different input schemes. In the example the player can switch between a keyboard and a controller input scheme while the game is running. See also `combine_contexts` which shows a different approach where both input schemes are active simultaneously instead of switching between them.\n- `mouse_position_2d` - Shows the built-in _mouse position_ input in combination with the _canvas coordinates_ modifier to get the mouse position in world space directly from the input system.\n- `mouse_position_3d` - Shows the built-in _mouse position_ input in combination with the _3d coordinates_ modifier to get the mouse position in 3D world space directly from the input system. Also shows how to use chorded triggers to rotate the camera with mouse movement while the right mouse button is held.\n- `quick_start` - This is the example that is also shown in the [Quick Start Guide]({{ site.baseurl }}/quick-start). It shows how to create a simple 2D character controller with G.U.I.D.E.\n- `remapping` - Shows how to use the built-in remapping system to allow players to remap their controls with a dialog.\n- `simple_input` - a very barebones example that shows how to create a simple input action and bind it to a key press.\n- `tap_and_hold` - Shows the built-in _tap_ and _hold_ trigger. Players can tap a button to jump or hold it to perform a somersault.\n- `top_down_shooter` - Shows how to handle input from controller or mouse to rotate a the player in a top-down shooter game. The demo shows two control schemes: one for controller, where the player aims with a stick and one for keyboard & mouse, where the player aims with the mouse and how these two schemes can be supported at the same time with very little code involved.\n- `touch` - Shows how to use the touch inputs and triggers to detect screen drags, taps, long-presses, pinch/zoom and rotation gestures with one or multiple fingers.\n- `two_joysticks` - Shows how to do local multiplayer with two joysticks. Each player has their own joystick and can move around independently while both players use the same script to handle input.\n- `virtual_cursor` - Shows how to control a cursor with a gamepad or keyboard and interact with UI elements using the virtual cursor utilities.\n- `virtual_sticks` - Shows on-screen virtual joysticks and buttons for touch devices, including custom renderers and configuration."
  },
  {
    "path": "docs/_docs/reference/04_input_reference.md",
    "content": "---\nlayout: page\ntitle: Input Reference\npermalink: /reference/inputs\ndescription: \"A reference for all built-in inputs.\"\n---\n\n## Introduction\nG.U.I.D.E ships with a selection of inputs for all kinds of input devices. This is a list of all built-in inputs and their settings:\n\n## Action\n\nThis input takes a `GUIDEAction` and returns the current value of the action. This way you can use an action value as input for another action. The input will actively track the state of the action and update the value accordingly. This means the input works independently of whether the action is currently bound to an active mapping context and it also works independently of the order in which the action in bound in a mapping context. This input will always mirror the full Vector3 of the action value (even if the action only uses parts of it). The input has the following settings:\n\n| Setting  | Description                             |\n|----------|-----------------------------------------|\n| _Action_ | The action that the input should track. |\n\n## Any\nThis input returns `(1,0,0)` if any input of the selected types is received, and `(0,0,0)` otherwise. This is useful to switch input schemes when you detect controller input from a certain device (e.g. switch to a controller or touch input scheme when receiving controller or touch input). Can also be used to realize things like \"press any key to continue\". The input has the following settings:\n\n| Setting                               | Description                                                                        |\n|---------------------------------------|------------------------------------------------------------------------------------|\n| _Mouse Buttons_                       | Detect input from mouse buttons.                                                   |\n| _Mouse Motion_                        | Detect mouse motion.                                                               |\n| _Minimum Mouse Movement Distance_     | The minimum distance the mouse must move to be considered actuated.                |\n| _Joy Buttons_                         | Detect input from joystick / joypad  buttons.                                      |\n| _Joy Axes_                            | Detect input from joystick / joypad axes.                                          |\n| _Minimum Joy Axis Actuation Strength_ | The minimum strength the joystick axis must be actuated to be considered actuated. |\n| _Keyboard_                            | Detect input from the keyboard                                                     |\n| _Touch_                               | Detect touch input                                                                 |\n\n## Joy Axis 1D\n\nThis input returns the value of a 1D axis of a joystick in the `x` component of the input value. The input is restricted to the range of `(-1, 0, 0)` to `(1, 0, 0)`, while usually being `(0,0,0)` when the axis is not actuated. Note that some joy axes only use positive values (e.g. controller triggers). The input has the following settings:\n\n| Setting | Description                                       |\n|---------|---------------------------------------------------|\n| _Axis_  | The joy axis that should be tracked by the input. |\n\n\n\n## Joy Axis 2D\nThis input returns the value of a 2D axis of a joystick in the `x` and `y` components of the input value. The input is restricted to the range of `(-1, -1, 0)` to `(1, 1, 0)`, while usually being `(0,0,0)` when the axis is not actuated. The input has the following settings:\n\n| Setting | Description                                                             |\n|---------|-------------------------------------------------------------------------|\n| _X_     | The joy axis that should be tracked by the input for the `x` component. |\n| _Y_     | The joy axis that should be tracked by the input for the `y` component. |\n\n\n## Joy Button\nThis input returns `(1,0,0)` if the selected joystick button is pressed, and `(0,0,0)` otherwise. The input has the following settings:\n\n| Setting  | Description                                         |\n|----------|-----------------------------------------------------|\n| _Button_ | The joy button that should be tracked by the input. |\n\n\n## Key\n\nThis input returns `(1,0,0)` if the selected key is pressed, and `(0,0,0)` otherwise. If modifiers are given, then both the key and all selected modifiers must be pressed in order for this input to return the actuated value of `(1,0,0)`. The input has the following settings:\n\n| Setting                      | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |\n|------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| _Key_                        | The key that should be tracked by the input.  This is expected to be a physical key code, so that the physical location of the key in the keyboard stays the same no matter which locale the keyboard is operating in.                                                                                                                                                                                                                                                         |\n| _Shift_                      | Whether the `Shift` modifier key must be pressed in addition to the main key.                                                                                                                                                                                                                                                                                                                                                                                                  | \n| _Control_                    | Whether the `Ctrl` modifier key must be pressed in adition to the main key.                                                                                                                                                                                                                                                                                                                                                                                                    |\n| _Alt_                        | Whether the `Alt` modifier key must be pressed in addition to the main key.                                                                                                                                                                                                                                                                                                                                                                                                    |\n| _Meta_                       | Whether the `Meta` modifier key (`Windows` key on Windows, `Cmd` on OSX) must be pressed in addition to the main key.                                                                                                                                                                                                                                                                                                                                                          |\n| _Allow additional modifiers_ | If true, the key input will actuate if modifiers in additional to the enabled ones are pressed. Let's assume you set up the key to `D` and disable _Shift_, _Control_, _Alt_ and _Meta_ and also disable _Allow additional modifiers_. Now the input will only actuate while the `D` key is pressed alone, but will not  actuate when any modifier key is pressed in addition to `D`. Usually this is not what you want, so _Allow additional modifiers_ is `true` by default. |                                                                                                                                                                      \n\n\n\n## Mouse Axis 1D\n\nThis input returns changes in mouse position along a single axis in the `x` component of the input value (e.g. `(x, 0, 0)`). The input is given in pixels per frame. You can use a _Screen Relative_ modifier to convert this into a value from -1 to 1 that is independent of the screen resolution. The input has the following settings:\n\n| Setting | Description                                         |\n|---------|-----------------------------------------------------|\n| _Axis_  | The mouse axis that should be tracked by the input. |\n\n## Mouse Axis 2D\n\nThis input returns changes in mouse position compared to the previous frame along two axes in the `x` and `y` components of the input value (e.g. `(x, y, 0)`). This is useful for first-person shooter mouse aiming. On most systems this value is given in raw mouse units, so it is resolution independent. Depending on the underlying operating system and whether the mouse cursor is currently visible, you may get viewport pixels. You can use the _Screen Relative_ modifier to convert the value to be resolution independent in this case.\n\n## Mouse Button\n\nThis input returns `(1,0,0)` if the selected mouse button is pressed, and `(0,0,0)` otherwise. Note, that Godot also treats the mouse wheel as buttons. If you need an axis from the wheel, you can\nmap _Wheel Up_ + a _Negate Modifier_ and _Wheel Down_ to a 1-dimensional action. The input has the following settings:\n\n| Setting  | Description                                           |\n|----------|-------------------------------------------------------|\n| _Button_ | The mouse button that should be tracked by the input. |\n\n\n## Mouse Position\n\nThis input returns the current mouse position in pixels relative to the top left corner of the window in the `x` and `y` components of the input value (e.g. `(x, y, 0)`). You an use a _Canvas Coordinates_ modifier to convert this into a value into 2D world coordinates for the currently active viewport or a _3D Coordinates_ modifier to convert this into 3D world coordinates (e.g. for object picking). This input has no settings.\n\n\n## Touch Angle\n\nThis input tracks changes in the angle between two touching fingers. This is mostly useful to detect rotation gestures. When the user touches the screen with two fingers the input will return `(0,0,0)`. If the user subsequently performs a rotation gesture, the input will return the angle changes since the beginning of the touch (e.g. `(<angle>,0,0)`). Once the user releases the touch, the input will return to `(0,0,0)`.  Note that when you use this input to rotate elements, that the input will retain the angle for as long as the fingers are touched. You should therefore save the starting rotation angle when the rotation begins and then apply the angle from the input on the starting value to show the user a preview of the rotation. Once the rotation ends, you can commit the value. This input has the following settings:\n\n| Setting | Description                                                   |\n|---------|---------------------------------------------------------------|\n| _Unit_  | The unit in which the angle is reported (degrees or radians). |\n\n\n## Touch Axis 1D\n\nThis input treats touch input like an axis, similar to the _Mouse Axis 1D_ input. When the user begins to touch the screen, and then drags, this input will return the delta value in pixels compared to the previous frame. You can use a _Screen Relative_ modifier to convert this into a value from -1 to 1 that is independent of the screen resolution. The value is returned in the `x` component of the input vector (e.g. `(<value>, 0, 0)`). If the user does not touch the screen this will return `(0, 0, 0)`.  This input has the following settings:\n\n| Setting        | Description                                                                                                                                                                                                            |\n|----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| _Axis_         | The axis that should be tracked by the input (_X_ or _Y_).                                                                                                                                                             |\n| _Finger Count_ | The amount of fingers that must be on the screen for this  input to react. The finger count must match exactly (e.g. if finger count is 2 then this input will only react while exactly 2 fingers are placed).         |\n| _Finger Index_ | The position of which finger should be used for calculating the offset. Use `0` for  the first finger, `1` for the second, and so on. You can also use `-1` which will use the average position of all active fingers. |\n\n## Touch Axis 2D\n\nSimilar to _Touch Axis 1D_  but returns input returns changes in touch position along two axes in the `x` and `y` components of the input value (e.g. `(x, y, 0)`). The input is given in pixels relative to the previous frame. You can use a _Screen Relative_ modifier to convert this into a value from -1 to 1 that is independent of the screen resolution. This input has the following settings:\n\n| Setting        | Description                                                                                                                                                                                                            |\n|----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| _Finger Count_ | The amount of fingers that must be on the screen for this  input to react. The finger count must match exactly (e.g. if finger count is 2 then this input will only react while exactly 2 fingers are placed).         |\n| _Finger Index_ | The position of which finger should be used for calculating the offset. Use `0` for  the first finger, `1` for the second, and so on. You can also use `-1` which will use the average position of all active fingers. |\n\n## Touch Distance\n\nThis input will track distance changes between two fingers. This is mostly useful to detect pinch/zoom gestures. When the user touches the screen with two fingers the input will return `(1,0,0)`. If the user subsequently performs a pinch or zoom gesture, the input will return a ratio of the current finger distance compared to the distance at the beginning of the touch (e.g. `(<distance ratio>, 0 ,0)`). At the beginning, this ratio is `1`. If the user makes a pinch gesture, the ratio become smaller, e.g. if the fingers distance is halved, the ratio will be `0.5`. If the user performs a zoom gesture, the ratio will grow, e.g. if the finger distance is doubled, the ratio will be `2.0`. Once the user releases the touch, the input will return to `(0,0,0)`.  \n\nLike with the _Touch Angle_ input, the input will retain the ratio for as long as the fingers are touched. You should therefore save the starting zoom/scale when the distance change begins and then apply the ratio from the input on the starting value to show the user a preview of the scale. Once the distance change ends, you can commit the value. This input has no settings.\n\n\n## Touch Position\n\nThis input returns the current position of a touching finger in pixels relative to the top left corner of the screen in the `x` and `y` components of the input value (e.g. `(x, y, 0)`). You an use a _Canvas Coordinates_ modifier to convert this into a value into 2D world coordinates for the currently active viewport or a _3D Coordinates_ modifier to convert this into 3D world coordinates (e.g. for object picking). If no finger is currently touching or the amount of touching fingers does not match _Finger Count_, the input is `(INF, INF, INF)`. You can use Godot's `is_finite()` method to check if the input is currently valid. G.U.I.D.E's triggers will treat this value as \"not actuated\". This input has the following settings:\n\n| Setting        | Description                                                                                                                                                                                                    |\n|----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| _Finger Count_ | The amount of fingers that must be on the screen for this  input to react. The finger count must match exactly (e.g. if finger count is 2 then this input will only react while exactly 2 fingers are placed). |\n| _Finger Index_ | The position of which finger should be reported. Use `0` for  the first finger, `1` for the second, and so on. You can also use `-1` which will use the average position of all active fingers.                |\n\n"
  },
  {
    "path": "docs/_docs/reference/05_modifier_reference.md",
    "content": "---\nlayout: page\ntitle: Modifier Reference\npermalink: /reference/modifiers\ndescription: \"A reference for all built-in modifiers.\"\n---\n\n## Introduction\n\nG.U.I.D.E ships with a selection of modifiers for many use cases. This is a list of all built-in modifiers and their\nconfiguration settings:\n\n## 3D Coordinates\n\nThis modifier converts a 2D mouse position (from the _Mouse Position_ input) into 3D world coordinates. It does this by\nperforming a raycast into the game world using the currently active 3D camera. Use this to find out \"where in my 3D\nworld did the player just click\". It has the following settings:\n\n| Setting              | Description                                                                                                                       |\n|----------------------|-----------------------------------------------------------------------------------------------------------------------------------|\n| _Max Depth_          | The maximum depth for the ray cast in units.                                                                                      |\n| _Collide with areas_ | Whether the raycast should collide with areas.                                                                                    |\n| _Collision mask_     | A mask used for the ray cast. This should be set up so the collision layers that make up your \"world\" are selected for collision. |\n\nThe modifier sets the action value to the 3D world position of the raycast hit. If the raycast hits nothing, the\nmodifier sets the action value to `Vector3.INF`.\n\n## 8-way direction\n\nThis is a filtering modifier that checks whether 2D input points into one of 8 directions. If the input points into this\ndirection, sets the action value to `(1,0,0)` otherwise to `(0,0,0)`. Can be used to get discrete directions from analog\ninput. It has the following setings:\n\n| Setting     | Description                                         |\n|-------------|-----------------------------------------------------|\n| _Direction_ | The direction for which the modifier should filter. |\n\n## Canvas coordinates\n\nThis modifier converts a 2D pixel coordinate (from the _Mouse Position_, _Touch Position_ or _Touch Axis_ inputs) into\n2D world coordinates (canvas coordinates) for the currently active viewport. This modifier takes into account the\ncurrent scaling mode and any changes (zoom, offset) applied by a Camera2D in the currently active viewport. Use this :\n- to quickly find out \"where in my 2D world did the player just click\" \n- to convert a change of screen coordinates into a\nchange of canvas coordinates (e.g., \"how far did the player move their finger on the touch screen in canvas coordinates since the last frame\").\n\nNote: This modifier treats the input as a position in pixels, or a position difference in pixels. This means you cannot meaningfully use this with inputs that only produce a change of an input vector (e.g., _Joy Axis 2D_). You can use a _Virtual Cursor_ modifier to convert such input into a virtual screen position and then pipe the output of the _Virtual Cursor_ into this modifier.\n\nThis modifier has the following settings:\n\n| Setting          | Description                                                                    |\n|------------------|--------------------------------------------------------------------------------|\n| _Relative Input_ | Whether the input is absolute (a position) or relative (a change of position). |\n\n## Curve\n\nThis modifier applies a separate curve to each input axis. Input values are assumed to fall in the range of `0` to `1`.\nYou can use the _Map Range_ modifier to convert your value into this range, if needed. It has the following settings:\n\n| Setting | Description                      |\n|---------|----------------------------------|\n| _Curve_ | The curve to apply.              |\n| _X_     | Apply the modifier to the X axis |\n| _Y_     | Apply the modifier to the Y axis |\n| _Z_     | Apply the modifier to the Z axis |\n\n## Deadzone\n\nThis modifier applies a deadzone to 1D, 2D or 3D axis input. You can specify a minimum and maximum value. Every input\noutside the minimum and maximum will be clamped while all the values between the minimum and maximum will be scaled to a\nrange between 0 and 1. This modifier has the following settings:\n\n| Setting           | Description                                                                         |\n|-------------------|-------------------------------------------------------------------------------------|\n| _Lower threshold_ | The lower threshold for the input. All values below this value will be mapped to 0. |\n| _Upper threshold_ | The upper threshold for the input. All values above this value will be mapped to 1. |\n\n## Input swizzle\n\nThis modifier rearranges the x,y and z components of the current input value. This is useful if you want to bind keys to\nmultiple axes or otherwise want to modify the input vector to do any operations on it. This modifier is often combined\nwith the _Negate_ modifier. It has the following settings:\n\n| Setting | Description                        |\n|---------|------------------------------------|\n| _Order_ | The new order of the input vector. |\n\n## Magnitude\n\nThis modifier outputs the magnitude (length) of the current input vector. The result is a 1D value provided on the `x`-axis; the `y` and `z` components are set to `0`. It has no settings.\n\n## Map Range\n\nThis modifier maps the input value through an input and output range and optionally clamps the output. Can be used to\nscale and translate at the same time.  (For example, mapping a `0` to `1` range to a `-1` to `1` range.) It has the\nfollowing settings:\n\n| Setting       | Description                                               |\n|---------------|-----------------------------------------------------------|\n| _Apply clamp_ | Whether the output should be clamped to the output range. |\n| _Input min_   | The input minimum.                                        |\n| _Input max_   | The input maximum.                                        |\n| _Output min_  | The output minimum.                                       |\n| _Output max_  | The output maximum.                                       |\n| _X_           | Apply the modifier to the X axis                          |\n| _Y_           | Apply the modifier to the Y axis                          |\n| _Z_           | Apply the modifier to the Z axis                          |\n\n## Negate\n\nThis modifier negates one or more axes of the input vector. Useful to map key inputs to negative values or to realize\ninverted controls for joysticks. It has the following settings:\n\n| Setting | Description                                          |\n|---------|------------------------------------------------------|\n| _X_     | Whether the `x`-axis of the input should be negated. |\n| _Y_     | Whether the `y`-axis of the input should be negated. |\n| _Z_     | Whether the `z`-axis of the input should be negated. |\n\n## Normalize\n\nThis modifier normalizes the input vector (e.g. keeps the direction but changes the length to 1). It has no settings.\n\n## Positive/Negative\n\nLimits inputs to positive or negative values. Values which do not match will be clamped to zero. Useful if you want to\nbind different halves of an axis to different actions. This modifier has the following settings:\n\n| Setting | Description                                                             |\n|---------|-------------------------------------------------------------------------|\n| _Range_ | Whether the the input should be limited to positive or negative values. |\n| _X_     | Whether the `x`-axis of the input should be affected.                   |\n| _Y_     | Whether the `y`-axis of the input should be affected.                   |\n| _Z_     | Whether the `z`-axis of the input should be affected.                   |\n\n## Scale\n\nMultiplies the input with the given Vector3. Useful to control things like input sensitivity or to convert input into a\nmore useful range (e.g. radians from 0-1). Input can also be multiplied with delta time (to limit input over time). The\nmodifier has the following settings:\n\n| Setting            | Description                                                                   |\n|--------------------|-------------------------------------------------------------------------------|\n| _Scale_            | A `Vector3` which is multiplied with the input vector.                        |\n| _Apply delta time_ | If checked, the input is additionally multiplied with the current delta time. |\n\n## Virtual Cursor (experimental)\n\nThis modifier provides a virtual 2D cursor that can be controlled by any input. This is useful if you need to simulate\nmouse input with a controller. The input will be treated as a 2D vector that moves the cursor around. The cursor will be\nclamped to the window size. Output of this modifier is the cursor position in pixels (similiar to what the _Mouse\nPosition_ input returns). Use the _3D coordinates_ or _Canvas coordinates_ modifier to convert the cursor position into\n3D or 2D world coordinates. This modifier has the following settings:\n\n| Setting                                   | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |\n|-------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| _Initial position_                        | The initial position of the cursor in a range between 0 and 1. This will be automatically scaled to the actual window size.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |\n| _Initialize from mouse position_          | If enabled, the initial cursor position is taken from the current mouse position when the modifier is activated. This overrides _Initial position_. Note: Has no effect while the mouse is captured, since the mouse is fixed to the window center.                                                                                                                                                                                                                                                                                                                                                                                               |\n| _Apply to mouse position on deactivation_ | When enabled, the current virtual cursor position is written back to the system mouse cursor when the modifier is deactivated. This helps avoid visible jumps when switching between a gamepad and a mouse. Note: Not supported while the mouse is captured.                                                                                                                                                                                                                                                                                                                                                                                      |\n| _Speed_                                   | The cursor movement speed in pixels.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |\n| _Screen Scale_                            | Screen scaling to be applied to the cursor movement. This controls whether the cursor movement speed is resolution dependent. The following settings are possible: <ul><li>_None_ - no scaling according to the screen resolution is done. An input of `(1,1,0)` will move the cursor one pixel down and one to the right.</li><li>_Longer Axis_ - input will be scaled by the longer axis of the screen resolution (e.g. x-axis in landscape modes, y-axis in portrait modes).</li><li> _Shorter Axis_ - Input will be scaled by the shorter axis of the screen resolution (e.g. y-axis in landscape modes, x-axis in portrait modes).</li></ul> |\n| _Apply delta time_                        | If checked, the input is additionally multiplied with the current delta time.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |\n\n## Window relative\n\nTreats the input vector as a mouse position difference in pixels (e.g. from the _Mouse_ input) and converts it into a\nrange from `(0,0)` to `(1,1)` relative to the current window size. Useful to get resolution-independent mouse\ndifferences. This modifier has no settings.\n\n**Note**: Depending on the operating system and input device this modifier may not be required and will in fact make\nyour input resolution-dependent when applied.\n\nYou already get resolution-independent values when:\n\n- using the mouse running on Linux\n- using the mouse running on OSX\n- using the mouse running on Windows with the mouse cursor captured\n\nIn these cases, the modifier should _not_ be applied.\n\nYou get resolution-dependent mouse values when:\n\n- using the mouse running on Windows with the mouse cursor visible\n- using touch input running on Android\n\nIn these cases the modifier can be applied. Other combinations have not been tested, so you may need to conduct your own\nresearch here and report your findings, so they can be added to the documentation.\n\n"
  },
  {
    "path": "docs/_docs/reference/06_trigger_reference.md",
    "content": "---\nlayout: page\ntitle: Trigger Reference\npermalink: /reference/triggers\ndescription: \"A reference for all built-in triggers.\"\n---\n\n## Introduction\nG.U.I.D.E ships with a selection of triggers for many use cases. This is a list of all built-in triggers and their configuration settings.\n\n## Chorded Action\nThis trigger is intended to be used in combination with other triggers. It works differently from other triggers as it will only allow the action to be triggered, if the chorded action is currently triggering. This is most often used to bind the same input to multiple different actions. For example the D-pad on a controller can do different things depending on whether the left trigger is held or not. Pressing down on the D-pad could consume a potion while holding the left trigger down and pressing down on the D-pad will open the inventory. To set this up, we will need to create three action mappings like this.\n\n![Chorded Action Example]({{site.baseurl}}/assets/img/manual/chorded_trigger_example.png)\n\n1. The chorded action itself. In this example, we have created an action named `dpad_switch` and this is bound to the left trigger switch of the controller. This action has no trigger set by default, so it will use a _Down_ trigger.\n2. The action to drink the potion. This is bound to the D-pad down and it uses a _Pressed_ trigger.\n3. The action to open the inventory. This is bound to the D-pad down as well, but it uses a _Chorded Action_ trigger that references the `dpad_switch` action (`#4`). Note that we still add a _Pressed_ trigger here because we only want to open the inventory when we hold the left trigger switch down and press down on the D-pad. If we didn't add the pressed trigger, the inventory would open if we only hold the left trigger switch down.\n\nIf multiple chorded action triggers exist, all of them must be triggering for the action to trigger. The chorded action trigger has the following settings:\n\n| Setting               | Description                                                            |\n|-----------------------|------------------------------------------------------------------------|\n| _Chorded action_      | The action that needs to be triggered for this action to be triggered. |\n| _Actuation Threshold_ | This setting has no effect for the chorded action trigger.             |\n\n## Combo\nThis trigger allows you to define a sequence of inputs that need to be triggered in order for the action to trigger. This is most often used in fighting games but can also be used to simulate other complex input sequences for example to trigger a magic spell. For the combo trigger you specify a list of actions that need to trigger in the given order. Each action can have a time window in which it needs to be triggered. If the time window is exceeded the combo will reset. Same happens if actions are entered out of order. In addition you can specify a list of cancel actions that will reset the combo if triggered. \n\n```mermaid\nstateDiagram\n    [*] --> None\n    None --> Ongoing: actuated\n    Ongoing --> Ongoing: input matches step and not last step\n    Ongoing --> Triggered: input matches step and last step\n    Ongoing --> None: input does not match step or cancelled\n    Triggered --> None\n```\n\nThe combo trigger has the following settings:\n\n| Setting               | Description                                                                                                                                                                                  |\n|-----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| _Enable Debug Print_  | If enabled, the trigger will print debug information about the current state of the combo to the console.                                                                                    |\n| _Steps_               | A list of actions that need to be triggered in order for the combo to trigger. For each action a time frame can be specified in which the action must be completed for the combo to succeed. |\n| _Cancel Actions_      | A list of actions that will cancel the combo immediately if triggered.                                                                                                                       |\n| _Actuation Threshold_ | This setting has no effect for the combo trigger.                                                                                                                                            |\n\n\n## Down\nThis is the most basic trigger. It triggers when the input is pressed down or any axis is non-zero. If you don't specify a trigger for an action then a down trigger with an actuation threshold of 0.0 will be used. \n\n```mermaid\nstateDiagram\n    [*] --> None\n    None --> Triggered: actuated\n    Triggered --> None: released\n```\n\nThe down trigger has the following settings:\n\n| Setting               | Description                                                                                                             |\n|-----------------------|-------------------------------------------------------------------------------------------------------------------------|\n| _Actuation Threshold_ | The threshold at which the trigger will activate. If the input value is above this threshold the trigger will activate. |\n\n\n## Hair\nThis trigger uses relative thresholds instead of absolute values, making it ideal for rapid-fire actions with analog inputs like trigger buttons. It triggers when the input rises by the threshold amount from its lowest point (valley), and releases when it drops by the threshold amount from its highest point (peak). This symmetric behavior prevents jitter and allows for quick re-triggering without having to fully release the input back to zero, similar to hair trigger modes found in competitive gaming controllers.\n\n```mermaid\nstateDiagram\n    [*] --> None\n    None --> Triggered: input rises by threshold from valley\n    Triggered --> None: input drops by threshold from peak\n```\n\nThe hair trigger has the following settings:\n\n| Setting               | Description                                                      |\n|-----------------------|------------------------------------------------------------------|\n| _Actuation Threshold_ | The relative amount the input must change to trigger or release. |\n\n## Hold\nThis trigger triggers when input is held down for a certain amount of time. An input is considered being held down if the input value is above the actuation threshold. \n\n```mermaid\nstateDiagram\n    [*] --> None\n    None --> Ongoing: actuated\n    Ongoing --> Triggered: _Hold Threshold_ exceeded\n    Ongoing --> None: released\n    Triggered --> None: released or _Is One Shot_ enabled\n```\n\nThe hold trigger has the following settings:\n\n| Setting               | Description                                                                                                                                                                                                                                                                                     |\n|-----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| _Hold Threshold_      | The time in seconds the input needs to be held down for the trigger to activate.                                                                                                                                                                                                                |\n| _Is One Shot_         | If enabled, the trigger will only trigger once when the _Hold Treshold_ is exceeded and will then need to be released before it can trigger again. If disabled, the trigger will continue to  trigger the action every frame after the _Hold Threshold_ is exceeded and while it is still held. |\n| _Actuation Threshold_ | The threshold at which the trigger will activate. If the input value is above this threshold the trigger will activate.                                                                                                                                                                         |\n\n## Pressed\nThis trigger triggers once when the input is pressed down. It will not trigger again until the input is released. This is the equivalent of Godot's `Input.is_action_just_pressed` function. \n\n```mermaid\nstateDiagram\n    [*] --> None\n    None --> Triggered: actuated\n    Triggered --> None\n```\n\nThe pressed trigger has the following settings:\n\n| Setting               | Description                                                                                                             |\n|-----------------------|-------------------------------------------------------------------------------------------------------------------------|\n| _Actuation Threshold_ | The threshold at which the trigger will activate. If the input value is above this threshold the trigger will activate. |\n\n## Pulse\nThis trigger triggers once when the input is pressed and will continue to trigger in configurable intervals as long as the input is held down. This can be used to simulate the OS key repeat functionality for any input. \n\n```mermaid\nstateDiagram\n    \n    [*] --> None\n    None --> Ongoing: actuated and _Trigger On Start_ disabled\n    None --> Triggered: actuated and _Trigger On Start_ enabled\n    Triggered --> Ongoing: _Max Pulses_ not reached\n    Triggered --> None: released or _Max Pulses_ reached\n    Ongoing --> Triggered: _Pulse Interval_ elapsed\n    Ongoing --> None: released\n```\n\n\nThe pulse trigger has the following settings:\n\n| Setting               | Description                                                                                                                          |\n|-----------------------|--------------------------------------------------------------------------------------------------------------------------------------|\n| _Trigger On Start_    | If enabled, the trigger will trigger immediately when the input is pressed, otherwise it will wait for the first interval to elapse. |\n| _Initial Delay_       | The initial delay after the initial actuation before pulsing begins.                                                                 |\n| _Pulse Interval_      | The interval in seconds at which the trigger will pulse after the initial delay.                                                     |\n| _Max Pulses_          | The maximum number of pulses that will be triggered. If set to a value <= 0, the trigger will pulse indefinitely.                    |\n| _Actuation Threshold_ | The threshold at which the trigger will activate. If the input value is above this threshold the trigger will activate.              |\n\n## Released\nThis trigger triggers once when the input is released. This is the opposite of the _Pressed_ trigger and works similar to Godot UI buttons `pressed` event (which - contrary to its name - is fired when the button is released). \n\n```mermaid\nstateDiagram\n    [*] --> None\n    None --> Ongoing: actuated\n    Ongoing --> Triggered: released\n    Triggered --> None\n```\n\n\nThis trigger has the following settings:\n\n| Setting               | Description                                                             |\n|-----------------------|-------------------------------------------------------------------------|\n| _Actuation Threshold_ | The threshold at which the trigger will consider the input as actuated. |\n\n## Stability\n\nThis trigger triggers depending on whether the input changes after being actuated. This is mostly useful for touch input to distinguish screen taps from screen drags.  \n\n```mermaid\nstateDiagram\n    [*] --> None\n    None --> Ongoing: actuated\n    Ongoing --> Triggered: stability criteria met\n    Ongoing --> None: released\n    Triggered --> None: released or stability criteria not met\n```\n\n\nThe trigger has the following settings:\n\n| Setting         | Description                                                                                                                                                                                                                                                                                                                                                                                                                          |\n|-----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| _Trigger When_  | There are two modes available. When set to _Input Is Stable_ will trigger while the input remains unchanged while still being actuated. This can be used to detect a screen tap where the finger should not move while being on the screen. The second mode is _Input Changes_. This will trigger when the input is actuated and then changes its value while remaining actuated. This can be used to detect a screen drag or flick. |\n| _Max Deviation_ | If the length of the input vector changes by more than this value, the input is considered \"changed\".                                                                                                                                                                                                                                                                                                                                |\n\n\n## Tap\nThis trigger triggers when the input is pressed and released within a certain time frame. Can be used with hold trigger to bind two different actions to the same input (tap for one action, hold for another).\n\n```mermaid\nstateDiagram\n    [*] --> None \n    None --> Ongoing: actuated\n    Ongoing --> Triggered: released\n    Ongoing --> None: _Tap Threshold_ exceeded\n    Triggered --> None\n```\n\nThe tap trigger has the following settings:\n\n| Setting               | Description                                                                                                                              |\n|-----------------------|------------------------------------------------------------------------------------------------------------------------------------------|\n| _Tap Threshold_       | The time that the player has to release the input after actuating it. If this time is exceeded, the input is not considered to be a tap. |\n| _Actuation Threshold_ | The threshold at which the trigger will activate. If the input value is above this threshold the trigger will activate.                  |\n\n"
  },
  {
    "path": "docs/_docs/usage/01_concepts.md",
    "content": "---\nlayout: page\ntitle: How G.U.I.D.E works\npermalink: /usage/concepts\ndescription: \"A high level overview of how G.U.I.D.E works.\"\n---\n\n## Introduction\n\nG.U.I.D.E is a plugin for Godot that allows you to create input actions and bind them to input events. It is designed to\nbe easy to use and flexible enough to cover a wide range of use cases. This document will give you a high level overview\nof how G.U.I.D.E works.\n\nThe general idea is that we have input actions that are bound to input events. This is very similar to how the input\nsystem in Godot works, but with some key differences. The main difference is that G.U.I.D.E uses not just a simple \"this\ninput is this action\" mapping. Instead, G.U.I.D.E provides a fully configurable input pipeline that receives input,\nprocesses it and then analyzes the input to determine which actions should be triggered. At a high level, this pipeline\nlooks like this:\n\n```mermaid\ngraph LR\n    A[Input] --> B[Modifier]\n    B --> C[Trigger]\n    C --> D[Action]\n```\n\n1. First, G.U.I.D.E receives input from Godot's input system.\n2. Then modifiers can be applied to this input (e.g. invert the input, scale it, apply a deadzone,etc.).\n3. Now the input is passed to a trigger. The trigger decides whether the input should trigger the action. There are\n   different triggers available, e.g. triggering on hold, press, tap, etc.\n4. If the trigger decides that the input should trigger the action, the action is triggered.\n\nG.U.I.D.E borrows this system from Unreal Engine's Enhanced Input System, but with some modifications to make it work\nnicely with Godot. So if you are familiar with Unreal Engine's enhanced input system, you will feel right at home with\nG.U.I.D.E.\n\n## Input\nAn input is piece of input data acquired from an input device. This is really just raw data and doesn't have any meaning by itself. All input is represented as a `Vector3` in G.U.I.D.E. Depending on the input device not all components of the `Vector3` will be used. For example keys and button presses will only use the `x` component, while analog sticks will use `x` and `y`. Specific input devices (like the accelerometer or XR controllers) might use all three components. G.U.I.D.E has support for most common input devices and will automatically handle the input data correctly.\n\n- **Keyboard** / **Mouse Buttons** / **Controller Buttons** - `x` is 1 if the key is pressed, 0 otherwise.\n- **Mouse relative movement** - `x` and `y` are the relative movement of the mouse.\n- **Analog sticks** - `x` and `y` are the position of the stick.\n\n## Modifiers\n\nA _modifier_ is a component that can modify the input before it is passed to the trigger. It takes the `Vector3` from the input and returns a modified `Vector3`. There are a variety of modifiers built into G.U.I.D.E, such as:\n\n- **Deadzone** - Removes input values that are below a certain threshold and remaps the remaining values to the range [0, 1].\n- **Invert** - Inverts the input values.\n- **Scale** - Scales the input values by a certain factor.\n\n## Triggers\n\nA _trigger_ is a component that decides whether an action should be triggered by input. There are a variety of triggers built into G.U.I.D.E, such as:\n\n- **Down** - Triggers every frame while the input is held down.\n- **Hold** - Triggers after the input has been held down for a certain amount of time.\n- **Press** - Triggers once when the input is pressed.\n- **Pulse** - Triggers repeatedly with a certain interval while the input is held down.\n- **Tap** - Triggers when the input is pressed and released quickly.\n\n\n\n## Actions\n\nAn _action_ is a named input event that can be triggered by input. An action has both a state and a value. The value of an action is the combination of all input values that are assigned to the action. This value can be used to determine the strength of the input (similar to Godot's built-in `get_action_strength`function).\n\n### Action states\n\nActions can be in one of three states:\n\n- **Completed** - The action is currently not active at all.\n- **Ongoing** - The triggers assigned to the action have detected input that can potentially trigger the action but not\n  all requirements have yet been satisfied.\n- **Triggered** - The action is currently active has been triggered by input.\n\n### Action state changes\n\nThe action's state is changed by the triggers that are assigned to this action in the mapping context. The trigger will\nlook at the input assigned to the action and then react to it. This reaction drives the action's state. Triggers have\nthe following reactions:\n\n- **None** - The trigger is not affected by the input.\n- **Ongoing** - The trigger is potentially affected by the input but is not yet triggered.\n- **Triggered** - The trigger is triggered by the input.\n\nThe combination of the action's state and the trigger's reaction to the input determines the action's state. The\nfollowing diagram shows how the action's state changes based on the trigger's reaction to the input:\n\n```mermaid\nstateDiagram\n    Completed --> Ongoing: Trigger \"Ongoing\"\n    Completed --> Triggered: Trigger \"Triggered\"\n    Ongoing --> Triggered: Trigger \"Triggered\"\n    Ongoing --> Completed: Trigger \"None\"\n    Triggered --> Ongoing: Trigger \"Ongoing\"\n    Triggered --> Completed: Trigger \"None\"\n```\n\nLet's look at an example to make this clearer. Lets say we have an _Interact_ action and we want this action to trigger\nwhen the player holds the controller's _A_ button down for 1 second.\n\nAt the beginning, the _Interact_ action is in the _Completed_ state. Now the player presses the _A_ button. The trigger\nassigned to the _Interact_ action will detect this input and react with _Ongoing_. This will in turn change the\n_Interact_ action's state to _Ongoing_.\n\n```mermaid\nstateDiagram\n    Completed --> Ongoing: Player presses \"A\"\n```\n\nThe player continues to hold the _A_ button. After 1 second, the trigger will react with _Triggered_. This will change\nthe _Interact_ action's state to _Triggered_.\n\n```mermaid\nstateDiagram\n    Ongoing --> Triggered: Player holds \"A\" for 1 second\n```\n\nIn instead the player would have released the _A_ button before the 1 second have passed, the trigger would have reacted\nwith _None_ and the _Interact_ action would have returned to the _Completed_ state.\n\n```mermaid\nstateDiagram\n    Ongoing --> Completed: Player releases \"A\" before 1 second\n```\n\n### Action signals\n\nActions can emit signals when they change their state. This allows you to react to the action's state changes in your\ncode. Events are emitted as Godot signals, so they can be connected to your code in the same way as other signals. The\nfollowing table shows the events that actions can emit:\n\n| Current State | New State | Signal           | Remarks                                            |\n|---------------|-----------|------------------|----------------------------------------------------|\n| Completed     | Ongoing   | `started`        | Only emitted for 1 frame.                          |\n| Completed     | Ongoing   | `ongoing`        |                                                    | \n| Completed     | Triggered | `triggered`      |                                                    |\n| Completed     | Triggered | `just_triggered` | Only emitted for 1 frame.                          |\n| Ongoing       | Ongoing   | `ongoing`        | Emitted every frame while action is ongoing.       |\n| Ongoing       | Triggered | `triggered`      |                                                    |\n| Ongoing       | Triggered | `just_triggered` | Only emitted for 1 frame.                          |\n| Ongoing       | Completed | `cancelled`      | Only emitted for 1 frame.                          |\n| Ongoing       | Completed | `completed`      |                                                    |\n| Triggered     | Ongoing   | `ongoing`        |                                                    |\n| Triggered     | Completed | `completed`      |                                                    |\n| Triggered     | Triggered | `triggered`      | Emitted every frame while the action is triggered. |\n\nThe following diagram shows the possible state transitions and the signals that are emitted:\n\n```mermaid\nstateDiagram\n    Completed --> Ongoing: started + ongoing\n    Completed --> Triggered: just_triggered + triggered\n    Ongoing --> Ongoing: ongoing\n    Ongoing --> Triggered: just_triggered + triggered\n    Ongoing --> Completed: cancelled + completed\n    Triggered --> Ongoing: ongoing\n    Triggered --> Completed: completed\n    Triggered --> Triggered: triggered\n```\n"
  },
  {
    "path": "docs/_docs/usage/02_configuration_and_input_handling.md",
    "content": "---\nlayout: page\ntitle: \"Configuring and input handling\"\npermalink: /usage/configuration-and-input-handling\ndescription: \"How to configure G.U.I.D.E and how to process input in your game code.\"\n---\n\n## Introduction\n\nG.U.I.D.E provides a lot of functionality and for many projects only a limited subset is actually needed. If you just want a quick introduction on how to use it check out the [quick start tutorial]({{site.baseurl}}/quick-start). \n\n## Actions\nActions are the main way in which our game code will interact with G.U.I.D.E. In contrast to Godot's built-in input actions, G.U.I.D.E actions are resources of type `GUIDEAction` which we create as files in our project. To create an action, simply right-click in Godot's file system explorer and then create a new resource:\n\n{% include video.html path=\"assets/img/manual/manual_create_action.mp4\" %}\n\nActions have quite a few properties which control how they work. We can edit the action's properties by double-clicking them, then we can edit their properties in the inspector.\n\n| Property                       | Description                                                                                                                                                                        |\n|--------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| _Name_                         | The name of the action. Is used when emitting the action as a Godot action and as a fallback when _Display Name_ is not set.                                                       |\n| _Action Value Type_            | The value type associated with the action. This is the only required configuration property.                                                                                       |\n| _Block Lower Priority Actions_ | If set this action will block other actions that are lower priority and share the same input. See [action input priority](#action-input-priority) below.                                       |\n| _Emit As Godot Actions_ | If checked whenever this action triggers, it will emit an `InputEventAction` into Godot`s input system. This is useful for interacting code that uses Godot actions (e.g. the UI). |\n| _Is Remappable_ | Marks the action as remappable. This allows remapping the action to new input at runtime.                                                                                          |\n| _Display Name_ | A display name to show the player for this action. Useful when creating UI for remapping input.                                                                                    |\n| _Display Category_ | A display category in which the action is located. Useful when creating UI for remapping input.                                                                                    |\n\n### Using actions in game code\n\nActions can be used in different ways in our game code. Before we can do that, we first need to get access to the action in our game code. The recommended way to do this is using an `@export` variable. This allows us to simply drag the action we want to access into the inspector.\n\n```gdscript\n@export var action:GUIDEAction \n```\n\nBecause an action is a resource, we can also get access to it by using Godot's `load` or `preload` functions:\n\n```gdscript\nvar action:GUIDEAction = load(\"res://path/to/my_action.tres\")\n```\n\n> Note: If you use `preload` to load an action, make sure you use `var` instead of  `const` for declaring the variable to store the action.\n> ```gdscript\n> # This will not work\n> const action:GUIDEAction = preload(\"res://path/to/my_action.tres\") \n> # This will work\n> var action:GUIDEAction = preload(\"res://path/to/my_action.tres\") \n> ```\n> This is a [bug](https://github.com/godotengine/godot/issues/101628) in Godot and there is currently no workaround for this.\n\n\nWe can now poll the action's state in `_process` or `_physics_process`:\n\n```gdscript\nfunc _process(delta:float) -> void:\n    if action.is_triggered():\n        print(\"The action was triggered!\")\n```\n\nAlternatively we can also get notified whenever the action changes its state. For this the action provides a range of [signals]({{site.baseurl}}/usage/concepts#action-signals).\n\n```gdscript\nfunc _ready():\n    action.triggered.connect(_on_action_triggered)\n    \nfunc _on_action_triggered():\n    print(\"The action was triggered\")\n```\n\nWe can use whichever way works best for our project. It is also possible to mix both ways. In addition to a state, each action also has a value. The type of value depends on the _Action Value Type_ we have set up when creating the action. We can access the current value of the action using the `value_xxx` properties:\n\n```gdscript\n# For boolean actions (on/off)\nvar action_value:bool = action.value_bool\n\n# For float actions (a single axis, e.g. joy trigger)\nvar action_value:float = action.value_axis_1d\n\n# For vector2 actions (two axes, e.g. joy stick or mouse position)\nvar action_value:Vector2 = action.value_axis_2d\n\n# For vector3 actions (three axes, e.g. 3D cursor)\nvar action_value:Vector3 = action.value_axis_3d\n```\n\nNote, that the value is independent of the state of an action. Even if an action is currently not triggered, it still has a value. \n\n## Mapping contexts\n\nMapping contexts allow us to assign input to actions. Like an action, a mapping context is also just a resource of type `GUIDEMappingContext`, so we can create one similar to how we create an action:\n\n![Creating a mapping context]({{site.baseurl}}/assets/img/manual/quick_start_create_mapping_context.png)\n\n\nIf we now double-click on the newly create mapping context, a custom editor will open and allow us to create bindings for our actions:\n\n![Mapping context editor]({{site.baseurl}}/assets/img/manual/manual_mapping_context_editor.png)\n\n### Creating action mappings\n\nTo create a new action mapping, press the _+_ button (#`1` in the image). This will create a new row for an action mapping. We start by dragging the action for which we want to create a binding into the action slot (#`2` in the image):\n\n{% include video.html path=\"assets/img/manual/quick_start_create_action_mapping.mp4\" %}\n\n### Adding input mappings\nNow we can create input mappings for our action. In the simplest case, an action is bound to a single piece of input (e.g. key on the keyboard, a button or a stick on the controller). To bind an input to our action, press the _+_ button next to the _Input Mappings_ list (#`3` in the image). We can now bind an input to the action by clicking the pen icon (#`4` in the image). The input dialog allows us to either detect an input or manually specify one:\n\n{% include video.html path=\"assets/img/manual/manual_input_dialog.mp4\" %}\n\nMost of the time we want to use the input detection as it is the quickest way to select an input. However some inputs (like _Action_ or _Any_) can only be selected manually on the right side of the dialog. Note, that the value type of the input doesn't necessarily need to match the value type of our action as the action value is calculated from the inputs (see [action value calculation](#action-value-calculation) below).\n\nIf we want to edit an input, we can either click the pen icon again and select a completely new input, or we can click on the input and then edit it in the inspector:\n\n{% include video.html path=\"assets/img/manual/manual_edit_inputs.mp4\" %}\n\nWe can also bind more than one input to the same action. The values of all inputs bound to an action will be combined into the final action value (see [action value calculation](#action-value-calculation) below).\n\n\n### Using actions as inputs\n\nSometimes it is useful to use the value of one action as input for another action. To do this, we can use the _Action_ input type. This allows us to select another action as input for the current action. The value of the input action will be used as the value of the current action. We can find the _Action_ input type on the right hand side of the input dialog in the _3D_ section. Once we have added the _Action_ input, click it to show it in the inspector. Then we can drag the action that we want to use as input into the _Action_ field.\n\n{% include video.html path=\"assets/img/manual/manual_add_action_input.mp4\" %}\n\n\nNote that action values are calculated in the order in which the actions are defined in the mapping context. So usually we will want to define the action that is used as input before the action that uses it as input. This also means that if we have a circular dependency between actions, then one of the actions will be calculated with the value of the other action from the previous frame. This can lead to unexpected behavior, so it is usually a good idea to avoid circular dependencies between actions.\n\n### Adding modifiers\n\nModifiers allow us to modify the value of an input before this value is sent to triggers and eventually our game code. For example, we can use a modifier to invert the value of an axis or to scale it. To add a modifier, click the _+_ button next to the _Modifiers_ list (#`5` in the image). We can now select a modifier from the list of available modifiers:\n\n{% include video.html path=\"assets/img/manual/manual_add_modifier.mp4\" %}\n\nMany modifiers have settings that allow us to configure how the modifier works. To change the settings of a modifier, click on the modifier and then edit the settings in the inspector:\n\n{% include video.html path=\"assets/img/manual/manual_edit_modifier.mp4\" %}\n\nWe can have multiple modifiers for a single input. The modifiers are applied in the order they are listed in the _Modifiers_ list. We can drag modifiers to change their order:\n\n{% include video.html path=\"assets/img/manual/manual_reorder_modifiers.mp4\" %}\n\n\n### Adding triggers\n\nTriggers control whether an action is currently triggered. We can add triggers by clicking the _+_ button next to the _Triggers_ list (#`6` in the image). We can now select a trigger from the list of available triggers:\n\n{% include video.html path=\"assets/img/manual/manual_add_trigger.mp4\" %}\n\nLike inputs and modifiers, triggers can have settings that allow us to configure how the trigger works. To change the settings of a trigger, click on the trigger and then edit the settings in the inspector:\n\n{% include video.html path=\"assets/img/manual/manual_edit_trigger.mp4\" %}\n\nFor the most part we will only ever use a single trigger per input mapping. However we can have multiple triggers for a single input mapping. If more than one trigger is defined for an input mapping, then at least one of the triggers must be active for the action to be triggered. The only exception to this rule is the _Chorded action_ trigger. If we have one or more _Chorded action_ triggers, then all of them must be active for the action to be triggered. If other triggers exist in addition to the _Chorded action_ trigger, then at least one of them must be triggering as well. We can reorder triggers by dragging them in the _Triggers_ list, however this currently has no effect on the behavior of the triggers, so this feature mostly exists so we can keep our triggers organized.\n\n\nAll triggers have a setting called _Actuation Threshold_. This setting determines at which value a trigger will deem the input to be actuated. The trigger will take the action value and compare it with the actuation treshold. If the action value is a vector, then the length of the vector will be compared with the threshold. The value of an action is calculated from its inputs and modifiers (see [action value calculation](#action-value-calculation) below).\n\n### Copying modifiers and triggers\n\nSometimes it can be useful to copy a trigger or modifier to another input mapping. We can achieve this by just dragging the modifier or trigger between input mappings. This will create a copy of the modifier or trigger (unless it is shared, see next section). Note that a modifier or trigger slot must already exist in the target input mapping for this to work. If none is there, we can just create a dummy modifier or trigger and then overwrite it.\n\n{% include video.html path=\"assets/img/manual/manual_copy_modifier_trigger.mp4\" %}\n\n### Sharing modifiers and triggers\n\nCopying modifiers and triggers is useful when we need a quick copy to change some settings. However sometimes we don't want a copy but rather a shared instance so we can make sure the settings are the same everywhere. We can achieve this by creating the modifier or trigger as a separate resource in the file system. Then we can drag this resource into the modifier or trigger slot of the input mapping. This will create a shared instance of the modifier or trigger. If we change the settings of the shared modifier or trigger, then all input mappings that use this shared modifier or trigger will be updated as well. The sharing is indicated by a different color in the slot.\n\n{% include video.html path=\"assets/img/manual/manual_shared_modifier_trigger.mp4\" %}\n\n**Important:** modifiers are stateless, while triggers are stateful. Because of this, G.U.I.D.E will create a duplicate of each trigger when enabling a mapping context. This is to ensure that the state of the trigger is not shared between different input mappings which would lead to unexpected behavior. This means when you change settings of a shared modifier through your game code (e.g. to enable/disable inversion of a stick) this will immediately work. However, if you change settings of a shared trigger through your game code (e.g. to change the actuation threshold or hold time) this will require that you disable and re-enable the mapping context to take effect.\n\n### Action value calculation\n\nEach action can have multiple inputs assigned to it. This raises the question, how the final value of an action is calculated. The calculation is relatively simple:\n\n1. Each input is evaluated to get a value.\n2. All modifiers are applied to the value.\n3. The values of all inputs are added together to get the final value of the action.\n\n\nFor example, say we have a 2D axis action which is driven by 4 inputs, the _W_, _A_, _S_ and _D_ keys:\n\n![Example 2D axis action]({{site.baseurl}}/assets/img/manual/manual_2d_axis_mapping.png)\n\n\nEach of these inputs is a `GUIDEInputKey` which will return a value of `(1,0,0)` if the key is pressed and `(0,0,0)` if the key is released. Say the player now presses the W and A keys at the same time. First the value of all inputs is calculated:\n\n- _W_: \n    - is pressed so we get `(1,0,0)`\n    - has a _Negate_ modifier which inverts the value to `(-1,0,0)`\n    - has an _Input Swizzle_ modifier which swaps the _X_ and _Y_ components, so the final value is `(0,-1,0)`\n- _A_:\n    - is pressed so we get `(1,0,0)`\n    - has a _Negate_ modifier which inverts the value to `(-1,0,0)`\n- _S_:\n    - is not pressed so we get `(0,0,0)`\n- _D_:\n    - is not pressed so we get `(0,0,0)`\n\nFinally all four values are added together to get the final value of the action:\n\n```\n(0,-1,0) + (-1,0,0) + (0,0,0) + (0,0,0) = (-1,-1,0)\n```\n\nSo the final value of the action is `(-1,-1,0)`. Because the action is set to be a 2D axis, the final value only contains the _X_ and _Y_ components. The _Z_ component is ignored.\n\n\n\n## Using mapping contexts in the game\n### Enabling and disabling mapping contexts\n\nWe can define multiple mapping contexts for our game, and we can enable and disable them through code while our game is running. This allows us to easily implement a few things which are rather difficult to do with Godot's built-in input system. \n\n- We can have contextual input depending on the player situation. E.g. the same controls that move the player left and right when the player is on foot, can control the steering of a car when the player is in a car.\n- We can quickly disable all input when we show a menu or a cutscene.\n- We can have different control schemes for different input devices (e.g keyboard and mouse vs. controller) and switch them dynamically when a certain input is actuated.\n\n\nMapping contexts are enabled and disabled with the `GUIDE` autoload.\n\n```gdscript\n@export var my_mapping_context:GUIDEMappingContext\n\nfunc _ready():\n    GUIDE.enable_mapping_context(my_mapping_context)\n\nfunc _disable_controls():\n    GUIDE.disable_mapping_context(my_mapping_context)\n\n```\n\nThe `enable_mapping_context` function also has a second boolean parameter `disable_others` which can be used to disable all currently active mapping contexts when enabling another. This can be useful in a variety of situations, for example when we want to switch to a different input scheme or when we want to disable all input while showing a menu. The default value for `disable_others` is `false`.\n\n```gdscript\n# Enable the mapping context and disable all other mapping contexts\nGUIDE.enable_mapping_context(my_mapping_context, true)\n```\n\nMapping contexts also provide an `enabled` and `disabled` signal which can be used to get notified when a mapping context is enabled or disabled. This can be useful for triggering additional actions when a mapping context becomes active or inactive. For example we can show or hide certain UI elements depending on the currently active mapping context.\n\n```gdscript\n@export var my_mapping_context:GUIDEMappingContext\n\nfunc _ready():\n    my_mapping_context.enabled.connect(_on_mapping_context_enabled)\n    my_mapping_context.disabled.connect(_on_mapping_context_disabled)\n\nfunc _on_mapping_context_enabled():\n    # Show some UI elements\n    ...\n    \nfunc _on_mapping_context_disabled():\n    # Hide some UI elements\n    ...\n```\n\n### Mapping context action priority\n\nWe can enable multiple mapping contexts at the same time. But what happens, if the same action is configured in multiple mapping contexts and these mapping contexts are all active? GUIDE allows us to specify a priority when activating a mapping context. The default priority is `0`, but we can set it to any integer value:\n\n```gdscript\nGUIDE.enable_mapping_context(my_mapping_context, false, -10)\n```\n\nLower values have higher priority. When multiple mapping contexts define mappings for the same action, GUIDE will merge the inputs from all active contexts together. This allows different contexts to contribute different inputs to the same action. For example, we could have a keyboard/mouse context that binds WASD to a \"move\" action, and a controller context that binds the left joystick to the same \"move\" action. When both contexts are enabled, the player can use either WASD or the joystick to move.\n\nIf mapping contexts have the same priority, the order is determined by when they were enabled - more recently enabled contexts take precedence. This is particularly useful when merging inputs, as it allows a later-enabled context to override specific inputs from an earlier context while still preserving other inputs.\n\nWhen merging inputs, GUIDE ensures that only one mapping exists for each input-action pair. If multiple contexts try to bind the same input to the same action, only the binding from the highest priority context is used. For contexts with the same priority, the more recently enabled context's binding takes precedence. For example, if both a keyboard context and a controller context try to bind the \"A\" button to a \"jump\" action, only one of those bindings will be active - the one from whichever context has higher priority (or was enabled more recently if they have the same priority).\n\nNote that input merging happens automatically when multiple contexts are enabled. If you want to completely replace all active mappings instead of merging them (for example, when switching from walking controls to flying controls), you can set the `disable_others` parameter to `true` when calling `enable_mapping_context()`. This will disable all previously active contexts before enabling the new one, saving you from having to manually call `disable_mapping_context()` for each active context.\n\n### Action input priority\n\nAn action can set the _Block Lower Priority Actions_ property. If this property is set, then triggering this action will block all other actions that are lower priority and share the same input. Action priority is determined by their position within the mapping context, so actions higher up in the list have higher priority. This behaviour is usually used in conjunction with the _Chorded action_ trigger to allow multiple actions to be triggered by the same input, based on whether a modifier key is pressed or not. For example on controllers, the D-pad can do different things depending on whether the left trigger is held or not. This could look like this in the mapping context editor:\n\n![Action input priority example]({{site.baseurl}}/assets/img/manual/manual_action_input_priority.png)\n\nHere we have a modifier action named `spell_toggle` that is bound to the left trigger (Axis 3 in Godot, #`1` in the image). Now we have two other actions `acid_enchantment` and `acid_bolt` (#`2` and #`4` in the image) which are both bound to D-pad up. In addition to the D-pad up input, `acid_enchantment` is also bound to the `spell_toggle` action with a _Chorded action_ trigger (#`3` in the image). This means that `acid_enchantment` will only trigger if the left trigger is held down **and** the D-pad up button is pressed. However `acid_bolt` will trigger every time if the D-pad up button is pressed regardless of the state of the left trigger (#`5` in the image). Therefore the `acid_enchantment` action has the _Block Lower Priority Actions_ property set to `true` and has been given a higher priority than the `acid_bolt` action. This way, when `acid_enchantment` is triggered, `acid_bolt` cannot be triggered at the same time because both actions share the same input and `acid_enchantment` has a higher priority.\n\n## The G.U.I.D.E debugger\n\nWhen designing input, it is often very useful to see what is happening with the input in real time. The G.U.I.D.E debugger allows us to see the current state of all actions and their inputs, so we can quickly see if our input is set up correctly. It also shows the calculated priorities when actions have overlapping input. \n\nThe G.U.I.D.E debugger is a separate scene that we can add to our game. It is recommended to put it into a separate canvas layer, but because it is a GUI control node, we could also embed it into a custom debugging UI. The debugger is a full scene, not just a single node so we need to use the `Instance Child Scene` option in the editor to add it to our scene:\n\n{% include video.html path=\"assets/img/manual/manual_add_guide_debugger.mp4\" %}\n\n\nThe debugger will automatically update as mapping contexts get enabled or disabled, so all we have to do is to add it to our game, and it should work without any additional configuration."
  },
  {
    "path": "docs/_docs/usage/03_input_prompts.md",
    "content": "---\nlayout: page\ntitle: \"Input prompts\"\npermalink: /usage/input-prompts\ndescription: \"How to display input prompts to the player.\"\n---\n\n## Introduction\n\nInput prompts like \"press A to jump\" tell your player how to control your game while being in the game. Showing input prompts is a great usability feature which will make it easy for your players to understand how to play your game. Therefore G.U.I.D.E provides a way to display input prompts in your game based on your configured actions.\n\nG.U.I.D.E itself does not mandate a specific way to display input prompts. Instead, you can use the built-in `GUIDEInputFormatter` class to build an input prompt string for any given action. You can then display this string in your game's user interface in any way you like. `GUIDEInputFormatter` can generate two kinds of input prompt strings:\n\n- Pure text strings, which are suitable for displaying input prompts as text.\n- Rich text strings, which are suitable for displaying input prompts as icons. These are intended to be used with Godot's `RichTextLabel` control.\n\n\n## General setup\n\nBefore you can get strings from `GUIDEInputFormatter`, you first need to get an instance of the `GUIDEInputFormatter` class.  There are two ways to do this:\n\n- You can get an instance of `GUIDEInputFormatter` with the `GUIDEInputFormatter.for_active_contexts()` factory method. This will return a formatter that will use the action mappings of all currently active mapping contexts. It will automatically keep track of changes to the active contexts, so you don't need to worry about updating the formatter when the active contexts change.\n- You can also create a formatter for a specific mapping context by calling `GUIDEInputFormatter.for_context(context)`. This will create a formatter that uses the action mappings of the specified context. This is mainly useful if you want to display input prompts in a key binding dialog where you want to show the prompts for a specific context.\n\n## Getting input prompt strings\n\nOnce you have an instance of `GUIDEInputFormatter`, you can use it to get input prompt strings for your actions. The `GUIDEInputFormatter` class provides multiple different methods to get input prompt strings. The simplest one is `action_as_text` which returns a text string for a given action\n\n```gdscript\n\n# The action for which we want to get the input prompt. \n@export var jump_action:GUIDEAction\n\n# The formatter that we will use to get the input prompt string.\nvar _formatter:GUIDEInputFormatter = GUIDEInputFormatter.for_active_contexts()\n\n# An example function that updates a label with the input prompt string for the jump action.\nfunc update_prompt_label():\n    var action_text:String = _formatter.action_as_text(jump_action)\n\n    $Label.text = tr(\"%s to jump\") % [action_text]\n```\n\nThe returned text will be a human-readable string that describes the input for the given action. For example, if the action is mapped to the `A` button on an XBox gamepad, the returned string will be `A`. If the action is mapped to the `Space` key on the keyboard, the returned string will be `[Space]`. The returned string will automatically reflect the actual input device being currently used, so you don't need to worry about displaying the correct input prompt for the player's input device. \n\nNote, that the returned string may contain more than one input, if an action is mapped to multiple inputs. For example if an action is mapped to the _W_, _S_, _A_, and _D_ keys on the keyboard, the returned string will be `[W], [S], [A], [D]`. Similar if the mapping is using a combo modifier, the returned string will be the combo, e.g. `A > B > A > X > Y`. If the action is mapped using chorded input, the returned string will be the chord, e.g. `LT + A`.\n\nAlso note, that the returned string will not contain a description of the action itself, only the input prompt. This is why in the example above we add the action description manually. `GUIDEInputFormatter` will automatically feed the input description through Godot's `tr` function so you can properly localize the prompt if desired.\n\n### Rich text strings\n\nIf you want to display input prompts as icons, you can use the `action_as_richtext_async` method. This method returns a BBCode string that can be directly fed into Godot's `RichTextLabel` control. It is important to know that the `GUIDEInputFormatter` will create these icons on the fly because there is quite a big amount of potential icons to be made (e.g. keyboard keys have labels in all kinds of different languages, so creating all possible icons beforehand really is not feasible). This means that this method may not return immediately but may take a frame or two to complete. You must therefore call this method asynchronously and update the `RichTextLabel` when the string is ready. Here is an example of how to use this method:\n\n```gdscript\n\nfunc update_prompt_label():\n    # Note that we use `await` here to wait for the result of the async method.\n    var action_text:String = await _formatter.action_as_richtext_async(jump_action)\n\n\n    $RichTextLabel.parse_bbcode(tr(\"%s to [b]jump[/b]\") % [action_text])\n\n```\n\n## Keeping the input prompt up-to-date\n\nWhen mapping contexts change, the player re-binds an input or joypads get connected and disconnected you will need to update the input prompt strings. G.U.I.D.E provides a signal to which your UI can subscribe to get notified when the input prompt strings need to be updated:\n\n\n```gdscript\nfunc _ready():\n    # Subscribe to the input_mappings_changed signal to get notified when the input mappings change.\n    GUIDE.input_mappings_changed.connect(_on_input_mappings_changed)\n\nfunc _on_input_mappings_changed():\n    # Update the prompt labels with the new input prompt strings.\n    ...\n```\n\n\n## An example implementation\n\nAll examples that come with G.U.I.D.E use a shared `RichTextLabel` to display the input prompts. This label has an `instructions_text` property that describes the actions that the player can perform. In a second `actions` property, the actual `GUIDEAction` objects are stored. \n\n![Input prompt label]({{site.baseurl}}/assets/img/manual/manual_instructions_label.png)\n\n\nAt runtime, the label will use the `GUIDEInputFormatter` to generate the input prompt strings for these actions and display them in the label. \n\n\n![Input prompt label in game]({{site.baseurl}}/assets/img/manual/manual_instructions_label_runtime.png)\n\n\nThe label will automatically update the input prompt strings when the input mappings change. This is not the only way to display input prompts, but it is a simple and effective way to do so and you can easily adapt it to your needs or use it as a starting point for your own implementation. You can find the full implementation of the input prompt label in the example projects that come with G.U.I.D.E at `guide_examples/shared/instructions_label.gd`. \n\n## Controlling the displayed controller icons\n\nWhen the player has connected a controller, you usually want to match the icons that are displayed on screen to the controller type that the player is using. G.U.I.D.E automatically tries to detect the type of controller that is connected and will show icons matching the general type of controller. So if the player uses a PlayStation controller, they will see PlayStation icons, and if they use an Xbox controller, they will see Xbox icons.\n\nSometimes it is not possible to properly detect the controller type. This is very often the case with off-brand controllers that just register as a generic controller to the operating system and have no useful detection strings that can identify them. This is also the case for web builds, where the browser hides the controller name from Godot and therefore Godot (and G.U.I.D.E) has no way to auto-detect the controller. \n\nIn these cases, G.U.I.D.E will by default show generic icons that will not match what is printed on the buttons of the controller. This is not a great user experience. Therefore, the input prompt system allows you to override which controller icons are displayed depending on a set of rules:\n\n```gdscript\nvar _formatter: GUIDEInputFormatter = GUIDEInputFormatter.new()\n\n# Instruct the formatter to detect the controller type and format the icons accordingly.\n# If the controller type cannot be detected, the formatter will use generic icons instead.\n# This is the default behavior.\n_formatter.formatting_options.joy_rendering = GUIDEInputFormattingOptions.JoyRendering.DEFAULT\n\n# Instruct the formatter to detect the controller type. If the controller type \n# cannot be detected, tell the formatter to use a specific controller type instead.\n_formatter.formatting_options.joy_rendering = GUIDEInputFormattingOptions.JoyRendering.PREFER_JOY_TYPE\n# In this case, tell the formatter to use the Microsoft controller type as fallback.\n_formatter.formatting_options.preferred_joy_type = GUIDEInputFormattingOptions.JoyType.MICROSOFT_CONTROLLER\n\n# Instruct the formatter to skip controller type detection and always use a specific \n# set of icons.\n_formatter.formatting_options.joy_rendering = GUIDEInputFormattingOptions.JoyRendering.FORCE_JOY_TYPE\n# In this case, tell the formatter to use Sony controller icons, always.\n_formatter.formatting_options.preferred_joy_type = GUIDEInputFormattingOptions.JoyType.SONY_CONTROLLER\n```\n\nYou can find an example of how to use this feature in the `input_prompts` example that ships with G.U.I.D.E.\n\n## Controlling the display of mixed inputs\n\nIn simple setups, it is quite common to just create an action and bind this action to multiple different inputs from different input devices. For example, this action is bound to the right mouse button, the Ctrl key on the keyboard, and the A button of a controller:\n\n![Mixed input]({{site.baseurl}}/assets/img/manual/manual_example_mixed_input.png)\n\nIf you create a string for this action, you will get something like this:\n\n![Mixed input string]({{site.baseurl}}/assets/img/manual/manual_mixed_input_string.png)\n\nNow, depending on your intent, this may or may not be what you would like to have. For example, if the player is free to change their input device at any time, you may want to only show the binding that matches the input device that the player has last used. To achieve this, you can set a filter in the input formatting options, that controls what kind of input is shown. The filter is a `Callable` which receives a formatting context and returns a boolean value:\n\n```gdscript\n\n# Tell the formatter to only render inputs related to the mouse and keyboard:\n_formatter.formatting_options.input_filter = \\\n    func (context:GUIDEInputFormatter.FormattingContext):\n        # context.input holds the input that we're about to format\n        # this is a GUIDEInput object.\n        # we use the device_type property of GUIDEInput to check \n        # if the input is related to the mouse or the keyboard.\n        return context.input.device_type & \\\n            (GUIDEInput.DeviceType.MOUSE | GUIDEInput.DeviceType.KEYBOARD) > 0\n\n\n# Tell the formatter to only render inputs related to joysticks/controllers:\n_formatter.formatting_options.input_filter = \\\n    func (context:GUIDEInputFormatter.FormattingContext):\n        return context.input.device_type & GUIDEInput.DeviceType.JOY > 0\n\n\n# Tell the formatter to render all inputs:\n_formatter.formatting_options.input_filter = \\\n    # this is the default filter,\n    GUIDEInputFormattingOptions.INPUT_FILTER_SHOW_ALL\n```\n\nYou can find an example of how to use this feature in the `input_prompts` example that ships with G.U.I.D.E. This example also has code to detect when the player changes their input device and update the input prompt strings accordingly.\n\n## Customizing the style of the input prompt icons\n\nG.U.I.D.E ships with a variety of renderers that can produce icons for all supported inputs. These renderers come with a built-in icon set that is good enough for prototyping or simple games where you don't have any specific needs to match the icons to the theme of your game. If you want to match the icons to your game, you have two options: \n\n- You can create a style for each renderer of G.U.I.D.E and apply this style when the game starts. \n- You can create a custom renderer that gives you complete freedom over how the icons are rendered. \n\n### Creating a style for each renderer\n\nRender styles are simply resources, so you can create them by right-clicking in the folder structure and then selecting _Create New..._ > _Resource_. Search for _RenderStyle_ in the search bar and then pick the style you want to create:\n\n![Render styles]({{site.baseurl}}/assets/img/manual/manual_render_styles.png)\n\nOnce you have created the style, you can double-click it and edit it in the inspector. Most styles will only allow you to change the icons for something else, but some styles like the key renderer style also allow you to change other aspects, like the font and the color of the key labels. \n\n![Render style example]({{site.baseurl}}/assets/img/manual/manual_render_style_example.png)\n\nOnce you have created and customized your render style, you can register it with G.U.I.D.E, and G.U.I.D.E will use your style over the built-in one. This can be done with one line of code.\n\n```gdscript\n# For joy render styles:\nGUIDEJoyRenderer.set_style(load(\"res://my_joy_render_style.tres\"))\n\n# For keyboard render styles:\nGUIDEKeyRenderer.set_style(load(\"res://my_keyboard_render_style.tres\"))\n\n# For mouse render styles:\nGUIDEMouseRenderer.set_style(load(\"res://my_mouse_render_style.tres\"))\n\n# For touch render styles:\nGUIDETouchRenderer.set_style(load(\"res://my_touch_render_style.tres\"))\n\n# For controller render styles, you can override the render style per controller type.\n# For Microsoft controllers\nGUIDEControllerRenderer.set_style(GUIDEControllerRenderer.ControllerType.MICROSOFT, \\\n    load(\"res://my_microsoft_controller_render_style.tres\"))\n\n# For Nintendo controllers    \nGUIDEControllerRenderer.set_style(GUIDEControllerRenderer.ControllerType.NINTENDO, \\\n    load(\"res://my_nintendo_controller_render_style.tres\"))\n\n# For Sony controllers    \nGUIDEControllerRenderer.set_style(GUIDEControllerRenderer.ControllerType.SONY, \\\n    load(\"res://my_sony_controller_render_style.tres\"))\n    \n# For Steam Deck controller\nGUIDEControllerRenderer.set_style(GUIDEControllerRenderer.ControllerType.STEAM_DECK, \\\n    load(\"res://my_steam_deck_controller_render_style.tres\"))\n```\n\n### Creating a custom renderer\n\nCreating a custom renderer is a bit more involved, but it allows you total freedom over how your icon looks and how it is put together. You can find more details about creating a custom renderer in the [Extending G.U.I.D.E]({{site.baseurl}}/usage/extending-guide#creating-custom-icon-renderers) section. \n\n"
  },
  {
    "path": "docs/_docs/usage/04_extending_guide.md",
    "content": "---\nlayout: page\ntitle: \"Extending G.U.I.D.E\"\npermalink: /usage/extending-guide\ndescription: \"How to write custom components for G.U.I.D.E\"\n---\n\n## Introduction\n\nG.U.I.D.E ships with a lot of built-in components that cover a wide range of use cases. However, you may have a certain use case that is specific for your game and is not covered by the built-in components. G.U.I.D.E is built in an extensible way so it is easy to add functionality that you need.\n\n## Creating custom inputs\n\nG.U.I.D.E can accept any `Vector3` as input, no matter where it comes from. So you can add your own custom inputs if you like to support a device that is not natively supported by G.U.I.D.E or to create a special input that behaves in a way that is useful for your game. To do this, create a new script derived from `GUIDEInput` and then implement the following functions:\n\n```gdscript\n# make sure the input is a tool script otherwise it will not show up\n# in the action mapping editor\n@tool \n\n# the class_name must be set as well\nclass_name MyCustomInput\nextends GUIDEInput\n\n# This function allows G.U.I.D.E to decide whether this input instance\n# is the same as another input. This is important to implement because\n# it allows G.U.I.D.E to deduplicate inputs that are shared between actions\n# and thus improves performance. It is also used during input \n# remapping to determine whether a binding has changed.\nfunc _is_same_as(other:GUIDEInput) -> bool:\n    return other is MyCustomInput\n    \n# This function should return a human readable name for the input.\n# The result from this function will be displayed in the \n# mapping context editor.\nfunc _editor_name() -> String:\n    return \"My Custom Input\"\n    \n# This function should return a short description of the input. The description\n# is shown as tooltip when hovering over the input.\nfunc _editor_description() -> String:\n    return \"An input that does something special.\"   \n\n# This function should return the value type of this input. The returned\n# value is used to put the input into the correct section in the \n# input mapping dialog.\nfunc _native_value_type() -> GUIDEAction.GUIDEActionValueType:\n    return GUIDEAction.GUIDEActionValueType.AXIS_3D\n```\n\nG.U.I.D.E should automatically find your new input and will offer it in the input dialog. Note that G.U.I.D.E will not be able to auto-detect your custom input, so you will need to select it from the right hand side of the input dialog.\n\n### Input workflow in G.U.I.D.E\n\nWhen a mapping context is activated, G.U.I.D.E will extract all used inputs from this mapping context and will instantiate one instance of each unique input. E.g. if we have two actions that check for the `A` key on the keyboard, only one instance of `GUIDEInputKey` checking for `A` will be created. This simplifies overlap detection and also improves performance. This is also why implementing the `_is_same_as` function is required.\n\nAfter that, the `_begin_usage` function is called on the input. Custom inputs can override this to implement some necessary setup operations or set an initial input value. Most built-in inputs use this to subscribe to G.U.I.D.E's globally managed input state, which is available in the `_state` variable. Your custom input can of course get the input events from anywhere else, but it is recommended to use the `_state` variable for any Godot input events, as this is the most efficient way to get input device updates.\n\n```gdscript\nfunc _begin_usage() -> void:\n    # subscribe to G.U.I.D.E's input state\n    _state.joy_button_state_changed.connect(_on_joy_button_state_changed)\n    # make sure to set the initial value\n    _on_joy_button_state_changed()\n\nfunc _on_joy_button_state_changed() -> void:\n   if _state.is_joy_button_pressed(_joy_index, _joy_button):\n       _value = Vector3(1, 0, 0)  # set the input value to pressed\n   else:\n       _value = Vector3.ZERO  # set the input value to not pressed\n```\n\nNow whenever input events are detected, `GUIDEInputState` will notify the input about relevant events. The inputs can now decide if and how this changes their `_value`. After the inputs are updated, G.U.I.D.E will then read the updated `_value`s and updates the action values and triggers. \n\nIf mapping contexts are changed and an input is no longer needed, G.U.I.D.E will destroy the instance and no longer call it. Before the input is destroyed, G.U.I.D.E calls `_end_usage`. This can be overridden by custom inputs to perform any cleanup operations, such as unsubscribing from signals:\n\n```gdscript\nfunc _end_usage() -> void:\n    # unsubscribe from G.U.I.D.E's input state\n    _state.joy_button_state_changed.disconnect(_on_joy_button_state_changed)\n```\n\n### Resetting input\n\nSometimes it may be necessary to reset input at the end of the frame. This is commonly the case for inputs the represent delta values (e.g. mouse movement in the current frame). By default, G.U.I.D.E will not reset inputs unless they explicitly ask for it - again to increase performance. If G.U.I.D.E should reset your input, you will need to implement a few functions:\n\n```gdscript\n# Override this function and make it return \"true\" to tell G.U.I.D.E that \n# this input needs to be reset at the end of the frame\nfunc _needs_reset() -> bool:\n    return true\n\n# This function will be called by G.U.I.D.E at the end of the frame when\n# _needs_reset return \"true\".  The default implementation sets the \n# _value vector to Vector3.ZERO but you can implement any other reset functionality\n# that is useful in your case.   \nfunc _reset() -> void:\n    ...\n\n```\n\n## Creating custom modifiers\n\nModifiers allow you to modify raw input and change its value. You can add custom modifiers to G.U.I.D.E if the built-in ones don't suit your needs. To do this, add a script deriving from `GUIDEModifier` to your project:\n\n```gdscript\n# The script needs to be tool, so G.U.I.D.E can detect it.\n@tool\n# The class name needs to be set as well.\nclass_name Tinyize\nextends GUIDEModifier\n\n# This function handles the modification. It gets the current input value\n# the delta time of the current frame and the value type of the \n# action to which this modifier is bound. It should return\n# the modified input.\nfunc _modify_input(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> Vector3:\n\t\n\treturn input * 0.01  # make the input really tiny\n\n# This function should return a human readable name for the modifier.\n# The result from this function will be displayed in the \n# mapping context editor.\nfunc _editor_name() -> String:\n\treturn \"Tinyize\"\t\n\n# This function should return a short description of the modifier. The description\n# is shown as tooltip when hovering over the modifier.\nfunc _editor_description() -> String:\n\treturn \"Makes the input really tiny.\"\n``````\n\nLike inputs, G.U.I.D.E will automatically detect your custom modifier and offer it in the mapping context editor.\n\n### Modifier workflow in G.U.I.D.E\n\nWhen a mapping context is activated, G.U.I.D.E will extract all used modifiers from this mapping context and will instantiate one instance of each modifier as needed. Contrary to inputs, modifiers will _not_ be deduplicated and therefore also don't have a `_is_same_as` method. This allows modifiers to be stateful - so they can save data across frames in internal variables and use this data to modify the input if needed.\n\nLike with inputs, modifiers can implement a `_begin_usage` function, that G.U.I.D.E calls once the modifier is activated and allows for some initialization code to be run.\n\nNow every frame G.U.I.D.E will first collect all input values from the defined inputs and then call the `_modify_input` value on all modifiers with the appropriate input value. The value that is returned will then be used to update the action values and triggers.\n\nIf mapping contexts are changed and a modifier is no longer needed, G.U.I.D.E will destroy the instance and no longer call it. Before the modifier is destroyed, G.U.I.D.E calls `_end_usage`. This can be overridden by custom modifiers to perform any cleanup operations.\n\n## Creating custom triggers\n\nCustom triggers allow you to implement your own trigger logic, if the built-in triggers do not suit your needs. To create a custom trigger, add a new script that derives from `GUIDETrigger` to your project:\n\n```gdscript\n# The script must be tool, so G.U.I.D.E can detect it.\n@tool\n# The class_name must be set as well.\nclass_name MyCustomTrigger\nextends GUIDETrigger\n\n# This function is called every frame with the current input value,\n# the delta time since the last frame and the value type of the\n# action to which this trigger is bound. The function should\n# return the updated state of the trigger.\nfunc _update_state(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> GUIDETriggerState:\n\t\n\t# the _is_actuated helper function will detect if the \n\t# input is considered to be actuated right now. It is\n\t# recommended to use this function instead of checking the\n\t# input vector manually, to ensure a consistent definition\n\t# of \"actuated\" across all inputs.\n\tif _is_actuated(input, value_type):\n\t\n\t\t# Triggers have a built-in _last_value property\n\t\t# which contains the input value as it was last frame.\n\t\t# This is useful in many cases which is why G.U.I.D.E will\n\t\t# keep this value up-to-date automatically.\n\t\tif not _is_actuated(_last_value, value_type):\n\t\t\treturn GUIDETriggerState.TRIGGERED\n\t\t\n\treturn GUIDETriggerState.NONE\n\n# This function should return a human readable name for the trigger.\n# The result from this function will be displayed in the \n# mapping context editor.\nfunc _editor_name() -> String:\n\treturn \"My Custom Trigger\"\n\n# This function should return a short description of the trigger. The description\n# is shown as tooltip when hovering over the trigger.\nfunc _editor_description() -> String:\n\treturn \"Trigger for one frame when the input is actuated.\"\n\n```\n\n### Trigger workflow in G.U.I.D.E\n\nWhen a mapping context is activated, G.U.I.D.E will extract all used triggers from this mapping context and will instantiate one instance of each trigger as needed. Like modifiers, triggers will _not_ be deduplicated and therefore also don't have a `_is_same_as` method. Triggers are stateful and preserve data across frames. For example, each trigger automatically comes with a `_last_value` property which holds the input value from the last frame. But triggers can of course also set up their own data as needed.\n\nNow every frame, G.U.I.D.E will call the trigger's `_update_state` method. The trigger can then update its internal state and should return the new trigger state:\n\n- `GUIDETriggerState.NONE` - if the trigger is currently not triggered. For example the built-in \"Hold\" trigger will return `NONE` if the input is currently not actuated at all.\n- `GUIDETriggerState.ONGOING` - if the trigger has detected some conditions that might trigger it, but not all conditions have been reached yet. For example the built-in \"Hold\" trigger will return `ONGOING` if the input is currently actuated, but the configured hold time has not elapsed yet.\n- `GUIDETriggerState.TRIGGERED` - if the trigger has detected that all conditions for triggering are met.  For example the built-in \"Hold\" trigger will return `TRIGGERED` if the input is actuated and the configured hold time has elapsed.\n\nBased on what the trigger returns, G.U.I.D.E will update the trigger state of the actions.\n\n### Trigger types\n\nTriggers can have one of three trigger types. These trigger types control how the action's final trigger state is calculated when multiple triggers are present on an action. \n\n- `EXPLICIT` - if explicit triggers are present, then **at least one** of these triggers must trigger for the action to be triggering. Almost all built-in triggers are of this type.\n- `IMPLICIT` - if implicit triggers are present, then **all** implicit triggers must trigger for the action to trigger. The _Chorded Action_ and _Stability_ triggers are of this type.\n- `BLOCKING` - if blocking triggers are present, then **none** of the blocking triggers must trigger for the action to trigger. So a blocking trigger will prevent the action from triggering. Currently, no built-in trigger uses this type, but it has been added for the sake of completeness.\n\nTo set the type for your trigger, override the `_get_trigger_type` function:\n\n```gdscript\n## Returns the trigger type of this trigger.\nfunc _get_trigger_type() -> GUIDETriggerType:\n    return GUIDETriggerType.IMPLICIT\n```\n\nThe default value is `EXPLICIT` so if your trigger should be explicit, you don't need to override this function.\n\n## Creating custom icon renderers\n\nIcon renderers turn inputs into small images that you can place inside UI text. They are used by the input formatter when you ask it to format an input or action as rich text.\n\nRendering is handled for you. Internally, G.U.I.D.E instantiates a small GUI scene and takes a screenshot of it to produce a texture (see Icon Maker). You only need to provide the scene and a script that knows how to arrange the UI for a specific input.\n\n### How to create a custom renderer\n\n1. Create a new scene for your icon UI.\n   - Put simple UI nodes (Control, TextureRect, Label, etc.) that visually describe the input.\n   - The root node should be a control or container. The size of the root control defines the icon size. G.U.I.D.E will take a screenshot of your root control at its current size and then scales it down to what was requested.\n2. Attach a script to the scene root that extends `GUIDEIconRenderer`.\n3. Implement three methods:\n   - `supports(input: GUIDEInput, options:GUIDEInputFormattingOptions) -> bool` — return true if this renderer can draw the given input.\n   - `render(input: GUIDEInput, options:GUIDEInputFormattingOptions) -> void` — show/hide or configure your UI elements so the icon represents the input.\n   - `cache_key(input: GUIDEInput, options:GUIDEInputFormattingOptions) -> String` — return a string that uniquely identifies the final look for this input. G.U.I.D.E uses this for disk caching.\n4. Optionally, set the `priority` export on your renderer. Lower values mean higher priority. \n\nA minimal example (see also `addons/guide/ui/renderers/keyboard/guide_key_renderer.gd` and `addons/guide/ui/renderers/controllers/guide_controller_renderer.gd`):\n\n```gdscript\n@tool\nextends GUIDEIconRenderer\n\n@onready var _label: Label = %Label\n\nfunc supports(input: GUIDEInput, options:GUIDEInputFormattingOptions) -> bool:\n    return input is GUIDEInputKey\n\nfunc render(input: GUIDEInput, options:GUIDEInputFormattingOptions) -> void:\n    var key: Key = input.key\n    var display_key: Key = DisplayServer.keyboard_get_label_from_physical(key)\n    _label.text = OS.get_keycode_string(display_key).strip_edges()\n    size = Vector2.ZERO\n    call(\"queue_sort\")\n\nfunc cache_key(input: GUIDEInput, options:GUIDEInputFormattingOptions) -> String:\n    # Use a stable prefix unique to your renderer plus what differentiates inputs\n    return \"my-key-renderer-v1\" + input.to_string()\n```\n\nTips:\n- Show or hide parts of the UI as needed in `render`. The controller renderer is a good example for toggling multiple elements.\n- Keep the `cache_key` stable across sessions for the same visual result so the on‑disk cache can be reused.\n- In many cases, using the input as a cache key is good enough. If you have additional input that goes into rendering an icon (e.g., a configurable icon style), this should also go into the calculation of the cache key. See the controller renderer for an example.\n\n### How renderers are selected\n\nWhen formatting an input as icons, G.U.I.D.E:\n- sorts all registered renderers by `priority` (smallest number first), then\n- asks them in order if they `supports` the input, and\n- uses the first renderer that returns `true`.\n\nThis means you can override built‑in behavior by using a smaller priority. G.U.I.D.E's built-in renderers have the following priority:\n\n- `GUIDEControllerRenderer`: `-10`\n- `GUIDEFallbackRenderer`: `100`\n- All other renderers: `0`\n\n### Registering your renderer\n\nCreate your renderer instance (usually by instantiating your scene) and register it once at startup:\n\n```gdscript\nvar my_renderer_scene := preload(\"res://path/to/my_renderer.tscn\")\nGUIDEInputFormatter.add_icon_renderer(my_renderer_scene.instantiate())\n```\n\nAfter registration, the renderer becomes part of the selection process described above. No further work is required. G.U.I.D.E will call your renderer when appropriate.\n\n## Creating custom text providers\n\nText providers turn inputs into short, human‑readable labels. They are used when you format an input or action as plain text (`GUIDEInputFormatter.action_as_text` / `input_as_text`). Use them to control wording per device family, platform, or game style.\n\nRendering is handled entirely by G.U.I.D.E. Your provider only decides what text to show for a specific input.\n\n### How to create a custom provider\n\n1. Create a new script that extends `GUIDETextProvider`.\n2. Implement two methods:\n   - `supports(input: GUIDEInput, options:GUIDEInputFormattingOptions) -> bool` — return true if this provider can label the given input.\n   - `get_text(input: GUIDEInput, options:GUIDEInputFormattingOptions) -> String` — return a concise label for that input. This is called only if `supports` returned true.\n3. Optionally, set the `priority` export on your provider. Lower numbers mean higher priority. The built‑in default text provider uses `0`. Specialized controller providers use negative priorities so they win over the default for matching devices. G.U.I.D.E will pick the provider with the smallest priority that supports the input.\n\nA minimal example that specializes keyboard labels:\n\n```gdscript\n@tool\nextends GUIDETextProvider\n\nfunc _init() -> void:\n    priority = -1  # has precedence over the default provider\n\nfunc supports(input: GUIDEInput, options:GUIDEInputFormattingOptions) -> bool:\n    return input is GUIDEInputKey\n\nfunc get_text(input: GUIDEInput, options:GUIDEInputFormattingOptions) -> String:\n    # Map certain keys to custom names, fall back to OS label\n    var key: Key = input.key\n    var display_key: Key = DisplayServer.keyboard_get_label_from_physical(key)\n    var label := OS.get_keycode_string(display_key).strip_edges()\n    if label == \"Enter\":\n        label = \"Return\"\n    return \"[%s]\" % label\n```\n\nTips:\n- Keep labels short and consistent. Stick to vocabulary your players expect.\n- If you need device‑specific naming, derive a small helper like the built‑in `controllers/controller_text_provider.gd` and override only the button names.\n- Do not include modifier logic (Shift/Ctrl) inside `get_text` for key inputs. G.U.I.D.E calls the provider separately for each part of a chord or combo.\n\n### How providers are selected\n\nWhen formatting text, G.U.I.D.E:\n- sorts all registered text providers by `priority` (smallest number first), then\n- asks them in order if they `supports` the input, and\n- uses the first provider that returns `true`.\n\nThis allows you to override the built‑in wording by using a smaller priority than the default provider. G.U.I.D.E's built-in providers have the following priority:\n\n- `GUIDEControllerTextProvider`: `-10`\n- `GUIDEDefaultTextProvider`: `0`\n\n### Registering your provider\n\nRegister your provider once at startup (e.g. in an autoload or your main scene):\n\n```gdscript\n# No need to keep a local instance. Providers are stored globally by the formatter.\nGUIDEInputFormatter.add_text_provider(MyCustomKeyTextProvider.new())\n```\n\nAfter registration, your provider participates in the selection process described above. See these references for concrete implementations:\n- `addons/guide/ui/text_providers/guide_default_text_provider.gd` — generic fallback labels\n- `addons/guide/ui/text_providers/controllers/guide_controller_text_provider.gd` — for controller‑specific naming\n"
  },
  {
    "path": "docs/_docs/usage/05_remapping_input.md",
    "content": "---\nlayout: page\ntitle: \"Remapping input at runtime\"\npermalink: /usage/remapping-input\ndescription: \"How to allow players to remap their controls at runtime.\"\n---\n\n## Introduction\n\nA very common use case in games is to allow players to remap their controls. G.U.I.D.E provides a built-in system to allow players to remap their controls at runtime with very little effort.\n\n## How remapping works\n\nWhen our game starts, we load a set of mapping contexts into G.U.I.D.E. These mapping contexts have the default input bindings for all actions, as we have defined them when creating the game. When we allow the player to remap inputs, we create a `GUIDERemappingConfig` - a resource that stores input bindings that the player has chosen. We can then feed this `GUIDERemappingConfig` into the `GUIDE` singleton to apply the changed input bindings on top of the currently active mapping contexts.\n\n```mermaid\ngraph LR\n    A[Mapping Contexts] --> B[Remapping Config]\n    B --> C[Effective Input Bindings]\n```\n\nSo the code for applying a remapping config could look somewhat like this:\n\n```gdscript\n## Path where the remapping config is saved. This file may\n## not exist if the player has not remapped their controls yet.\nconst REMAPPING_CONFIG_PATH:String = \"user://remapping_config.tres\"\n\nfunc _ready():\n    # Load the remapping config if it was previously saved.\n    if ResourceLoader.exists(REMAPPING_CONFIG_PATH):\n        var remapping_config:GUIDERemappingConfig = load(REMAPPING_CONFIG_PATH)\n        GUIDE.set_remapping_config(remapping_config)\n\n    # Add or change the mapping contexts as needed.\n    GUIDE.enable_mapping_context(some_mapping_context)\n```\n\nNote, that the remapping config will remain active until we set a new one. So we can enable and disable mapping contexts as needed and the remapping will still be applied on top of the currently active mapping contexts.\n\n## Marking actions for remapping\n\nBy default, G.U.I.D.E actions cannot be remapped. This is because only the developer can know which actions are suitable for remapping and it usually is not desirable to have every `GUIDEAction` to be remappable. We can mark actions for remapping in the inspector:\n\n![Remapping settings]({{site.baseurl}}/assets/img/manual/manual_action_remapping_settings.png)\n\nIn there, we have three settings:\n\n- **Is Remappable**: If this is enabled, the action can be remapped.\n- **Display Name**: This is a human-readable name for the action which we can later show in our remapping dialog.\n- **Display Category**: This is a category for the action which we can later show in our remapping dialog.\n\nUsing a _Display Name_ and _Display Category_ is optional, but it will help us implement the remapping dialog later.\n\n### Actions with multiple inputs\n\nSome actions may have more than one input assigned to them, for example when using WASD for creating a 2D movement action. In this case, we have the possibility to override the _Display Name_ and _Display Category_ for each input that is assigned to the action:\n\n![Remapping settings]({{site.baseurl}}/assets/img/manual/manual_input_mapping_remap_override.png)\n\nTo do this, we press the settings icon on the left hand side of the input mapping (#`1` in the image above). This will open the input mapping in the inspector. Now we can check the _Override action settings_ checkbox (#`2`). Now we can set the _Display Name_ and _Display Category_ for this specific input mapping (#`3`). If we leave a field empty, the value from the action will be used. We can also uncheck the _Is Remappable_ checkbox to disable remapping for this specific input mapping if needed.\n\n\n\n\n## Creating a remapping dialog\n\nWhen the game first starts, we don't have a remapping config yet and the game will use the default controls that the developer set up in the design phase. So we need to create a dialog that allows the player to remap their controls. This dialog should show all actions that are marked as remappable and allow the player to assign new inputs to them. When the dialog is closed, it should produce a `GUIDERemappingConfig` that can be saved to disk. \n\nBecause the remapping dialog is very game-specific, G.U.I.D.E does not provide a built-in dialog for this. You will have to create your own dialog that fits the style of your game. However, G.U.I.D.E provides a few helpers to make this easier.\n\nThe first helper is the `GUIDERemapper` class, which provides a convenient interface to get all remappable actions and their current input bindings, apply new input bindings, check the input mappings for conflicts and produce a `GUIDERemappingConfig` from the current input bindings.\n\nLet's first create an instance of `GUIDERemapper`:\n\n```gdscript\nvar _remapper:GUIDERemapper = GUIDERemapper.new()\n\n# The remapper needs to be initialized with _all_ mapping contexts that our \n# game uses which contain remappable actions. We can load these in any way\n# either with `load` or using an @export var in our dialog scene.\n\n@export var mapping_contexts:Array = []\n\n# We also need the current remapping config to initialize the remapper, so\n# the remapper knows which inputs have already custom bindings.\n\nvar remapping_config:GUIDERemappingConfig = load(REMAPPING_CONFIG_PATH)\n\nfunc _ready():\n    _remapper.initialize(mapping_contexts, remapping_config)\n```\n\nNow we have a functional remapper that knows about all remappable actions and their current input bindings. We can now use this remapper to create a dialog that allows the player to remap their controls.\n\n### Getting remappable items\n\nTo get all remappable items, we can use the `get_remappable_items` method:\n\n```gdscript\nvar remappable_items:Array[GUIDERemapper.ConfigItem] = _remapper.get_remappable_items()\n```\n\nThis will return an array of `GUIDERemapper.ConfigItem` objects. Each of these config items represents one action or input mapping that can be remapped. The `ConfigItem` has the following properties which we can use to display the items in our dialog:\n\n- `mapping_context` - The mapping context from which the action or input mapping comes.\n- `action` - The action to which the input mapping belongs. \n- `index` - The index of the input mapping in the action. This is useful if the action has multiple input mappings.\n- `display_category` - The display category of the action or input mapping. This automatically handles overrides from the input mapping.\n- `display_name` - The display name of the action or input mapping. This automatically handles overrides from the input mapping.\n- `value_type` - the value type of the input (e.g. `BOOL`, `AXIS_1D`, `AXIS_2D`, etc.). This is useful later when detecting the input, to limit input to the correct type.\n- `is_remappable` - Whether the action or input mapping is remappable. If we got the item from `get_remappable_items`, this will always be `true`, but later when we check for conflicts, this might be `false`. \n\n#### Filtering remappable items\n\nYou may want to filter the remappable items to only show certain categories or only items for a specific action or mapping context. The `get_remappable_items` method has three optional parameters that provide predefined filters:\n\n```gdscript\n# Filter all items in the keyboard mapping context.\nvar items := _remapper.get_remappable_items(keyboard_mapping_context)\n\n# Filter all items in the \"Movement\" category.\nvar items := _remapper.get_remappable_items(null, \"Movement\")\n\n# Filter all items for the \"Move\" action.\nvar items := _remapper.get_remappable_items(null, null, move_action)\n\n# We can of course also use a completely custom filter by iterating over all items\n# and filtering them ourselves using the array filter method or a simple for loop.\nvar items := _remapper.get_remappable_items() \\\n    .filter(func(item): return item.display_category == \"Movement\")\n\n```\n\nNow with these remappable items, we can create a user interface which shows all the items that can be remapped and allows the user to re-bind them. \n\n### Getting the currently bound input for an item\n\nWhen we create the this interface, we need to show the current input that is bound to an action. We can get this information from the remapper:\n\n```gdscript\nvar current_input:GUIDEInput = _remapper.get_bound_input_or_null(item)\n```\n\nThis will return the current input that is bound to the action or input mapping. If no input is bound (because the player has deliberately unbound this input), it will return `null`.\n\nNow we have a `GUIDEInput` object, but we need to format this in a way the player can understand. We can use the `GUIDEInputFormatter` class that we also use for [displaying input prompts]({{site.baseurl}}/usage/input-prompts) in our game. Like with the input prompts, we can either use a plain text representation or an icon representation. \n\n```gdscript\n# Get the formatter for the input. The 48 is the size of the icon we want to use.\n# If we only want to use text, we can leave this out.\nvar formatter:GUIDEInputFormatter = GUIDEInputFormatter.new(48)\n\n# Get the formatted input in text form\nvar input_text:String = formatter.input_as_text(current_input)\n\n# Get the formatted input as an icon. This will return a bbcode string \n# that we can use in a RichTextLabel.\nvar input_as_icon:String = await formatter.input_as_richtext_async(current_input)\n``` \n\nNote, that because the icon is generated on the fly, this may take a few frames to complete. So we need to use `await` to wait for the icon to be generated.\n\nSo a very simple way to build the interface could look like this:\n\n```gdscript\n# get a formatter\nvar _formatter:GUIDEInputFormatter = GUIDEInputFormatter.new(48)\n\nfunc _build_interface() -> void:\n    # get all items\n    var items:Array[GUIDERemapper.ConfigItem] = _remapper.get_remappable_items()\n    \n    # We now use a GridContainer to display the items in a grid.\n    var grid:GridContainer = $GridContainer\n    \n    for item in items:\n        var current_input:GUIDEInput = _remapper.get_bound_input_or_null(item)\n    \n        # add a rich text label to the grid which shows the bound input as an icon\n        var label:RichTextLabel = RichTextLabel.new()\n        grid.add_child(label)\n\n        # apply the formatted input as an icon to the label\n        _apply_input(current_input, label)\n        \n        # the item's bound input can change at any time and we\n        # don't want to re-build this interface every time, so we\n        # subscribe to the item's input changed signal to update the\n        # label when the input changes.\n        item.changed.connect(_apply_input.bind(label))\n        \n        # add the display name as a label to the grid\n        var name_label:Label = Label.new()\n        name_label.text = item.display_name\n        grid.add_child(name_label)\n\n# This function applies the input to a label. If the input is null, it will\n# show a grayed out \"Not bound\" text.\nfunc _apply_input(input:GUIDEInput, label:RichTextLabel) -> void:\n    if input == null:\n        label.parse_bbcode(\"[color=gray]Not bound[/color]\")\n        return\n        \n    var icon:String = await _formatter.input_as_richtext_async(input)\n    label.parse_bbcode(icon)\n```\n\nIn a real game this will likely be a bit more sophisticated, but this example should just show the general idea. You can find a slightly larger example of a remapping dialog that works for mixed keyboard/controller inputs in the `remapping` example that ships with G.U.I.D.E.\n\n### Detecting input\n\nNow that we have the current input configuration on screen, the player should be able to re-bind the input. For this, we somehow need to detect the input that the player wants to bind. To help with this, G.U.I.D.E provides the `GUIDEInputDetector` node. This node can be added to our dialog scene and will detect input from supported devices. We can add this node anywhere in our remapping dialog, as it isn't visible to the player.\n\nThe `GUIDEInputDetector` node has a few settings that we can use to configure it:\n\n![Input Detector Settings]({{site.baseurl}}/assets/img/manual/manual_guide_input_detector.png)\n\n\n- **Detection Countdown Seconds** - an amount of seconds the detector will wait before it starts detecting input. This is useful to give the player a moment to prepare before they start pressing buttons.\n- **Minimum Axis Amplitude** - the minimum amplitude an axis must have to be considered as actuated. This is useful to prevent accidental actuations by drifting sticks.\n- **Abort Detection On** - this is a list of `GUIDEInput` objects representing inputs that will abort the detection. This is useful if we want to allow the player to cancel the detection by pressing a specific button. In the example the player can cancel the detection by pressing the `ESC` key on the keyboard or the `Back` button on a controller.\n- **Use Joy Index** - when a joystick/gamepad event is detected, this setting determines which joystick index should be reported. The default is `Any` which will result in a binding that works for any attached joystick/gamepad. We can also set this to `Detected` which will return a binding that exactly matches the joystick/gamepad which was detected. This is useful if we want to allow the player to bind inputs to a specific joystick (for example in local multiplayer modes where two players use two joysticks to control the game).\n\nNow that we have the `GUIDEInputDetector` node in our scene, we can start detecting input. We can start the detection by calling the `detect` method:\n\n```gdscript\n@onready var input_detector:GUIDEInputDetector = %InputDetector\n\nfunc _rebind(item:GUIDERemapper.ConfigItem) -> void:\n    input_detector.detect(item.value_type)\n```\n\nWe need to tell the input detector which value type we would like to detect (e.g. `BOOL`, `AXIS_1D`, `AXIS_2D`, etc.). This is important because our input mapping usually expects a specific input value type, because we have modifiers that work on this value type. We can use the value type we get from the `ConfigItem` that we acquired from the remapper. After we called `detect`, the input detector will start the detection countdown and then wait for input. You may want to show a message to the player that they should press the button they want to bind.\n\n#### Limiting input to device types\n\nDepending on how our remapping dialog is set up, we may want to limit the input to specific device types. For example, if we have a dialog that has one page for keyboard and mouse and another page for gamepad, we may want to limit the input to the device type that is currently active. To limit the input to a specific device type, we can specify this in the `detect` method:\n\n```gdscript\n# Detect only keyboard and mouse input\ninput_detector.detect(item.value_type, \\ \n    [GUIDEInputDetector.DeviceType.KEYBOARD, GUIDEInputDetector.DeviceType.MOUSE])\n```\n\n### Handling the detected input\n\nWhen the input detector has detected an input, it will emit the `input_detected` signal. We can connect to this signal to handle the detected input. A very simple way to do this is to simply `await` the signal right after we started the detection:\n\n```gdscript\nfunc _rebind(item:GUIDERemapper.ConfigItem) -> void:\n    input_detector.detect(item.value_type)\n\n    var input:GUIDEInput = await input_detector.input_detected\n\n    # The input variable now either contains the detected input\n    # or is null if the detection was aborted.\n```\n\nNow we have the detected input and feed it back into the remapper to apply the new input binding:\n\n```gdscript\nfunc _rebind(item:GUIDERemapper.ConfigItem) -> void:\n    ...\n    if input != null:\n        _remapper.set_bound_input(item, input)\n```\n\n### Checking for collisions\n\nSetting the input binding like we just did is very simple, but it doesn't check for collisions. For example, if the player has already bound the `A` key to move left and now also binds the `A` key to jump, we have a collision or conflict. The game might still work, but now pressing `A` will trigger both actions. Also, the player might bind input that is reserved for other actions, like the `ESC` key which is usually used to open the pause menu.\n\nTo prevent this, we should check for collisions before we set the input binding:\n\n```gdscript\nfunc _rebind(item:GUIDERemapper.ConfigItem) -> void:\n    ...\n    # if the detection was aborted, there is no input to check for conflicts.\n    if input == null:\n        return\n        \n    # check for collisions. This will return an array of all items that\n    # collide with the input we want to bind.\n\tvar collisions:Array[GUIDERemapper.ConfigItem] = _remapper.get_input_collisions(item, input)\n\t\t\n\t# if any collision is from a non-remappable mapping, we cannot use this input\n\t# at all, so we abort the binding.\n\tif collisions.any(func(it:GUIDERemapper.ConfigItem): return not it.is_remappable):\n\t\treturn\n\t\t\n\t# We resolve the collisions by unbinding the conflicting input.\n\tfor collision in collisions:\n\t\t_remapper.set_bound_input(collision, null)\n\t\t\n\t# And finally we set the new input.\n\t_remapper.set_bound_input(item, input)        \n```\n\nThis code does a very simple collision handling - it just unbinds any conflicting input, except if the conflicting input is not remappable (so the player cannot overwrite the non-remappable `ESC` binding for the menu). Depending on your game, you might want to handle collisions differently. For example, you could show a warning to the player that the input they are trying to bind is already used for another action and ask them if they want to overwrite the existing binding or cancel the binding. \n\nAlso note that because we used the `ConfigItem`'s `changed` signal to update the interface when the input changes, the interface will automatically reflect the results of our collision resolution. So we don't need to do anything else to update the interface to reflect the new input bindings.\n\n## Saving the remapping config\n\nWhen the player has finished remapping their controls, we need to save the remapping config to disk. \n\n```gdscript\n# Get the new remapping config from the remapper\nvar new_remapping_config:GUIDERemappingConfig = _remapper.get_remapping_config()\n\n# Save the remapping config to disk\nResourceSaver.save(REMAPPING_CONFIG_PATH, new_remapping_config)\n\n# Apply the new remapping config to the GUIDE singleton\nGUIDE.set_remapping_config(new_remapping_config)\n```\n\nBecause a remapping config is a resource, it is also possible to have multiple remapping configs and switch between them. So your remapping dialog could allow the player to save multiple remapping configs as input profiles and switch between them in the game settings.\n\n"
  },
  {
    "path": "docs/_docs/usage/06_virtual_joysticks.md",
    "content": "---\nlayout: page\ntitle: \"Virtual Joysticks\"\npermalink: /usage/virtual-joysticks\ndescription: \"How to use virtual joysticks with G.U.I.D.E\"\n---\n\n## Overview\n\nVirtual joysticks allow you to create on-screen controls that can be used to interact with your game. This is often used in mobile games where the player usually cannot use physical controllers and only has a touchscreen. If you want to see this in action, check out the `virtual_sticks` example scene that ships with G.U.I.D.E.\n\n## Adding a virtual joystick to your scene\n\nVirtual joysticks and buttons are UI components. Usually they are overlaid on top of your game scene. It is recommended that you use a `CanvasLayer` as the root node for your virtual joysticks and buttons. Then you can use Godot's anchoring system to position and anchor them to the screen, so they work with any resolution.\n\n{% include video.html path=\"assets/img/manual/manual_add_virtual_joys.mp4\" %}\n\nYou will notice that by default, nothing is visible when you add a virtual joystick or button. This is because how the joystick looks is very dependent on the game that you are making. That's why there is a separate component for rendering joysticks and joy buttons. We'll get to that in a minute. Both the joystick and the joy button have a debug mode which will show you the size of the joystick or button on screen, and you can use that to position them at the right position for your game.\n\nAfter adding a joystick or button to your scene, you can configure various options. Adjust its size and decide whether it works with a mouse, touch or both. Also, choose which virtual device the button or joystick should belong to. This will become important later when we try to map the virtual joystick in our G.U.I.D.E mapping context.\n\n### Input mode\n\nVirtual joysticks and joy buttons can work in three different input modes:\n\n- _Mouse_ - the joystick or button will respond to mouse input.\n- _Touch_ - the joystick or button will respond to touch input.\n- _Mouse and touch_ - the joystick or button will respond to both mouse and touch input.\n\nIf you use _Mouse and touch_ as input mode, make sure that Godot's _Emulate mouse from touch_ and _Emulate touch from mouse_ options are disabled, otherwise you will get unexpected behavior. Also note, that when using _Mouse_ you can only ever use one joystick or button at a time, as there is only a single mouse cursor. \n\n## Rendering virtual joysticks and joy buttons\n\nBecause the style of your virtual joysticks and joy buttons should match the style of your game, G.U.I.D.E allows you to fully control how this is rendered on screen. The rendering is controlled by two custom nodes:\n\n- `GUIDEVirtualStickRenderer` - controls the appearance of the joystick\n- `GUIDEVirtualButtonRenderer` - controls the appearance of the joy button\n\nBoth of these nodes come with a default implementation which uses textures to render the stick and the button. So a simple way to get started is to use these default renderers and change the textures to match your game. To do this, add the matching renderer node as a child of the virtual stick or virtual button. It will then automatically update when the stick or button changes its state.\n\n{% include video.html path=\"assets/img/manual/manual_virtual_stick_default_renderers.mp4\" %}\n\nYou can customize the textures in the inspector of each renderer.\n\n## Using virtual joysticks in G.U.I.D.E mapping contexts.\n\nVirtual joysticks send `InputEventJoypadMotion` while virtual joy buttons send `InputEventJoypadButton` events to the input system. They use virtual device ids between -2 and -6 to avoid conflicts with real joysticks. This means that you can use virtual joysticks in the same way as real joysticks, so you will map them to `GUIDEInputJoyAxis2D` and `GUIDEInputJoyButton` inputs. The only difference is that you select the virtual joystick in the input mapping context.\n\n![Using virtual joys in the mapping context]({{site.baseurl}}/assets/img/manual/virtual_joy_mapping_context.png)\n\n## Creating custom renderers\nIf the built-in default renderer, which uses textures, is not enough for your game, you can create your own renderers and fully control the rendering process. So if you want to use a fancy shader or animations, you can totally do this.\n\n### Creating a custom virtual stick renderer\n\nTo create a custom virtual stick renderer, create a new script and derive it from the `GUIDEVirtualStickRenderer` class. Virtual stick renderers are controls, and they are supposed to live below the virtual stick node, which is a center container node. This means you don't need to care about positioning your controls as long as they are children of the renderer. They will get positioned automatically, even if the virtual stick changes its position. The `GUIDEVirtualStickRenderer` base class will provide you with two methods, which you can override to update your renderer whenever the stick changes its position or its setup.\n\n```gdscript\nclass_name MyCustomVirtualStickRenderer\nextends GUIDEVirtualStickRenderer\n\n## This is called when the virtual stick changes its configuration, e.g. size.\nfunc _on_configuration_changed() -> void:\n    # Called when the virtual stick changes its configuration, e.g. size.\n\n    # you can get the new configuration from\n    # the base class' properties:\n    var new_stick_radius := stick_radius\n    var new_max_actuation_radius := max_actuation_radius\n    \n    # update custom UI elements or shaders here\n    ...\n\n## This is called when the virtual stick changes its position or actuation status.\nfunc _update(joy_position: Vector2, joy_offset:Vector2, is_actuated:bool) -> void:\n    # joy_position is the position of the joystick\n    # relative to the center of the virtual stick in\n    # world coordinates.\n    # joy_offset is the position of the joystick\n    # relative to the center of the virtual stick\n    # in a normalized coordinate system (e.g from (-1, -1) to (1, 1)).\n    # is_actuated is true if the joystick is actuated.\n    \n    # update custom UI elements or shaders here\n    ...\n```\n\nYou can study the built-in default virtual stick renderer to see a working example. If you'd like to see a more complex example, check out the `virtual_sticks` example scene that ships with G.U.I.D.E. This uses a custom virtual stick renderer to render a virtual joystick with a shader.\n\n### Creating a custom virtual button renderer\nA virtual button renderer works similarly to a virtual stick renderer. You can create a custom virtual button renderer by deriving from the `GUIDEVirtualButtonRenderer` class.\n\n```gdscript\nclass_name MyCustomVirtualButtonRenderer\nextends GUIDEVirtualButtonRenderer\n\n## This is called when the virtual button changes its configuration, e.g. size.\nfunc _on_configuration_changed() -> void:\n    # Called when the virtual button changes its configuration, e.g. size.\n   var new_button_radius := button_radius \n\nfunc _update(is_actuated:bool) -> void:\n    # is_actuated is true when the button is actuated,\n    # false otherwise\n    \n    # update custom UI elements or shaders here.\n```\n\n"
  },
  {
    "path": "docs/_docs/usage/07_recipes.md",
    "content": "---\nlayout: page\ntitle: \"Recipes\"\npermalink: /usage/recipes\ndescription: \"A collection of recipes for G.U.I.D.E\"\n---\n\n## Introduction\nG.U.I.D.E is a powerful tool that can be used in many different ways. While a documentation can only cover what is available, it doesn't really tell you how to use it to solve real world problems. Therefore this page contains a collection of recipes that show you how to use G.U.I.D.E to solve common problems. \n\n## Recipes\n{% for page in site.recipes %}\n- [{{ page.title }}]({{site.baseurl}}{{ page.url }}) - {{ page.description }}\n{% endfor %}"
  },
  {
    "path": "docs/_includes/alert.html",
    "content": "<div class=\"alert alert-{{ include.type }}\" role=\"alert\">\n<h4 class=\"alert-heading\">{% if include.title %}{{ include.title }}{% else %}{{ include.type }}{% endif %}</h4>\n{{ include.content }}\n</div>\n"
  },
  {
    "path": "docs/_includes/doc.html",
    "content": "<a href=\"{{ site.baseurl }}/docs/{{ include.path }}\">{% if include.name %}{{ include.name }}{% else %}{{ include.path }}{% endif %}</a>\n"
  },
  {
    "path": "docs/_includes/editable.html",
    "content": "<a href=\"{{ site.repo }}/edit/{% if site.github_branch %}{{ site.github_branch }}{% else %}master{% endif %}/{{ page.path }}\"\n     target=\"_blank\"><i class=\"fa fa-edit fa-fw\"></i> Editar página</a>\n<a href=\"{{ site.repo }}/issues/new?labels={% if page.editable %}{{ page.editable }}{% else %}question{% endif %}&title=Question:&body=Question on: {{ site.repo }}/tree/master/{{ page.path }}\"\n     target=\"_blank\"><i class=\"fab fa-github fa-fw\"></i> Criar issue da página</a>\n<a href=\"{{ site.repo }}/issues/new\" target=\"_blank\"><i class=\"fas fa-tasks fa-fw\"></i> Criar issue do projeto</a>\n<!-- this will parse through the header fields and add a button to open\n     an issue / ask a question on Github. The editable field should be in\n     the post frontend matter, and refer to the label to open the issue for -->"
  },
  {
    "path": "docs/_includes/feedback.html",
    "content": "{% if site.feedback %}<style>\n  .feedback--answer {\n    display: inline-block;\n  }\n  .feedback--answer-no {\n    margin-left: 1em;\n  }\n  .feedback--response {\n    display: none;\n    margin-top: 1em;\n  }\n  .feedback--response__visible {\n    display: block;\n  }\n</style>\n<h5 class=\"feedback--title\">Feedback</h5>\n<p class=\"feedback--question\">Was this page helpful?</p>\n<button class=\"feedback--answer feedback--answer-yes\">Yes</button>\n<button class=\"feedback--answer feedback--answer-no\">No</button>\n<p class=\"feedback--response feedback--response-yes\">\n  Glad to hear it! Please <a href=\"{{ site.repo }}/issues/new\">tell us how we can improve</a>.\n</p>\n<p class=\"feedback--response feedback--response-no\">\n  Sorry to hear that. Please <a href=\"{{ site.repo  }}/issues/new\">tell us how we can improve</a>.\n</p>\n<script>\n  const yesButton = document.querySelector('.feedback--answer-yes');\n  const noButton = document.querySelector('.feedback--answer-no');\n  const yesResponse = document.querySelector('.feedback--response-yes');\n  const noResponse = document.querySelector('.feedback--response-no');\n  const disableButtons = () => {\n    yesButton.disabled = true;\n    noButton.disabled = true;\n  };\n  const sendFeedback = (value) => {\n    if (typeof ga !== 'function') return;\n    const args = {\n      command: 'send',\n      hitType: 'event',\n      category: 'Helpful',\n      action: 'click',\n      label: window.location.pathname,\n      value: value\n    };\n    ga(args.command, args.hitType, args.category, args.action, args.label, args.value);\n  };\n  yesButton.addEventListener('click', () => {\n    yesResponse.classList.add('feedback--response__visible');\n    disableButtons();\n    sendFeedback(1);\n  });\n  noButton.addEventListener('click', () => {\n    noResponse.classList.add('feedback--response__visible');\n    disableButtons();\n    sendFeedback(0);\n  });\n</script>{% endif %}<br/>\n\n"
  },
  {
    "path": "docs/_includes/footer.html",
    "content": "<footer class=\"bg-dark row d-print-none\">\n  <div class=\"footer-container\">\n        <small class=\"text-white\">\n          © {{ 'now' | date: \"%Y\" }} {{ site.author }} <br>\n          <a style=\"color: #e18e39\" href=\"{{ site.repo }}\">GitHub Repository</a>\n        </small>\n        {% if site.privacy %}<small class=\"ml-1\"><a href=\"{{ site.privacy }}\" target=\"_blank\">Privacy Policy</a></small>{% endif %}\n  </div>\n</footer>\n"
  },
  {
    "path": "docs/_includes/google-analytics.html",
    "content": "{% if site.google-analytics %}<script async src='https://www.google-analytics.com/analytics.js'></script>\n<script type=\"application/javascript\">\nvar doNotTrack = false;\nif (!doNotTrack) {\n\twindow.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;\n\tga('create', '{{ site.google-analytics }}', 'auto');\n\tga('send', 'pageview');\n}\n</script>{% endif %}\n"
  },
  {
    "path": "docs/_includes/head.html",
    "content": "<head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n    <meta name=\"generator\" content=\"Hugo 0.55.6\" />\n\n    <META NAME=\"ROBOTS\" CONTENT=\"INDEX, FOLLOW\">\n\n    <link rel=\"alternate\" type=\"application/rss&#43;xml\" href=\"/docs/index.xml\">\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"{{ site.baseurl }}/img/logo.svg\" sizes=\"32x32\">\n\n    <title>{{ site.title }} | {{ page.title | default: site.title }}</title>\n    <meta property=\"og:title\"\n        content=\"{{ site.title }} | {{ page.title | default: site.title }}\" />\n    <meta property=\"og:description\"\n        content=\"{{ page.description | default: site.description }}\" />\n    <meta property=\"og:type\" content=\"website\" />\n    <meta property=\"og:url\" content=\"{{ site.baseurl }}\" />\n    <meta property=\"og:site_name\" content=\"{{ site.baseurl }}\" />\n    <meta property=\"og:image\" content=\"{{ site.baseurl }}/assets/img/logo.svg\" />\n\n    <meta itemprop=\"name\" content=\"{{ page.title | default: site.title }}\">\n    <meta itemprop=\"description\"\n        content=\"{{ page.description | default: site.description | strip_html | strip_newlines | truncate: 160 }}\">\n\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <meta name=\"twitter:title\" content=\"{{ site.title }} | {{ page.title | default: site.title }}\" />\n    <meta name=\"twitter:description\"\n        content=\"{{ page.description | default: site.description | strip_html | strip_newlines | truncate: 160 }}\" />\n    <meta property=\"twitter:image\" content=\"{{ site.baseurl }}/assets/img/logo.svg\" />\n\n    <link rel=\"stylesheet\" href=\"{{ site.baseurl }}/assets/css/main.css\">\n    <link rel=\"stylesheet\" href=\"{{ site.baseurl }}/assets/css/palette.css\">\n\n    <!-- jQuery -->\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js\" integrity=\"sha512-+NqPlbbtM1QqiK8ZAo4Yrj2c4lNQoGv8P79DPtKzj++l5jnN39rHA/xsqn8zE9l0uSoxaCdrOgFs6yjyfbBxSg==\" crossorigin=\"anonymous\" referrerpolicy=\"no-referrer\"></script>\n\n    <!-- Bootstrap -->\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/bootstrap/4.6.2/js/bootstrap.min.js\" integrity=\"sha512-7rusk8kGPFynZWu26OKbTeI+QPoYchtxsmPeBqkHIEXJxeun4yJ4ISYe7C6sz9wdxeE1Gk3VxsIWgCZTc+vX3g==\" crossorigin=\"anonymous\" referrerpolicy=\"no-referrer\"></script>\n    <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/bootstrap/4.6.2/css/bootstrap.min.css\" integrity=\"sha512-rt/SrQ4UNIaGfDyEXZtNcyWvQeOq0QLygHluFQcSjaGB04IxWhal71tKuzP6K8eYXYB6vJV4pHkXcmFGGQ1/0w==\" crossorigin=\"anonymous\" referrerpolicy=\"no-referrer\" />\n    <!-- Font Awesome -->\n    <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/solid.min.css\" integrity=\"sha512-LopA1sokwAW/FNZdP+/5q8MGyb9CojL1LTz8JMyu/8YZ8XaCDn1EOm6L7RWIIOHRM7K4jwnHuOmyLZeeeYxSOA==\" crossorigin=\"anonymous\" referrerpolicy=\"no-referrer\" />\n    <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/fontawesome.min.css\" integrity=\"sha512-TPigxKHbPcJHJ7ZGgdi2mjdW9XHsQsnptwE+nOUWkoviYBn0rAAt0A5y3B1WGqIHrKFItdhZRteONANT07IipA==\" crossorigin=\"anonymous\" referrerpolicy=\"no-referrer\" />\n    <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/brands.min.css\" integrity=\"sha512-sKhd1NGM4i4pJj+3P+NVHisu2z5rKAwNG1IpWMdKsFWYlUHFSrsAO3geQ5QNKttkMPZNTo76tfg8jVx2ICP7qw==\" crossorigin=\"anonymous\" referrerpolicy=\"no-referrer\" />\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/js/fontawesome.min.js\" integrity=\"sha512-txsWtB+FOLDRFFsBL75QF7cPI4rqSjVH7Q+jKuaLrEI+uPPfvNfX66+pHF/4pU4pgQS3ptJ25xOvC8Erm+P+rA==\" crossorigin=\"anonymous\" referrerpolicy=\"no-referrer\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/js/brands.min.js\" integrity=\"sha512-LGx1r1hs7PQc2npcsY2jKTwbFubaKcgnks+jSm+LfEIhCI6P31GMq6MINmqkkL+JCTD9fJISBIwQdVOkeq9miw==\" crossorigin=\"anonymous\" referrerpolicy=\"no-referrer\"></script>\n</head>\n"
  },
  {
    "path": "docs/_includes/header.html",
    "content": "<header>\n    <nav style=\"background: #2c2c2c;\" class=\"js-navbar-scroll navbar navbar-fixed-top navbar-dark flex-column flex-md-row td-navbar\">\n        <a class=\"navbar-brand\" href=\"{{ site.baseurl }}/\">\n                <img style=\"max-height: 34px; margin-left: 12px; vertical-align: midle; margin-right: 12px; display:inline;\" src=\"{{ site.baseurl }}/assets/img/logo.svg\" alt=\"logo\">\n            <span style=\"vertical-align: middle; font-size: 32px;\">\n                {{ site.title }}\n            </span>\n        </a>\n        <div style=\"margin-left: auto;\" class=\"d-none d-md-block\">\n            <a href=\"{{ site.repo }}\">\n                <i class=\"fab fa fa-github fa-3x\" style=\"color: white; padding-right:12px; float:left; margin-top:5px\"></i>\n            </a>\n        </div>\n    </nav>\n</header>\n"
  },
  {
    "path": "docs/_includes/navigation.html",
    "content": "<svg class=\"md-svg\">\n<defs>\n  <svg>\n  <path d=\"M160 304q0 10-3.125 20.5t-10.75 19-18.125 8.5-18.125-8.5-10.75-19-3.125-20.5 3.125-20.5 10.75-19 18.125-8.5 18.125 8.5 10.75 19 3.125 20.5zM320 304q0 10-3.125 20.5t-10.75 19-18.125 8.5-18.125-8.5-10.75-19-3.125-20.5 3.125-20.5 10.75-19 18.125-8.5 18.125 8.5 10.75 19 3.125 20.5zM360 304q0-30-17.25-51t-46.75-21q-10.25 0-48.75 5.25-17.75 2.75-39.25 2.75t-39.25-2.75q-38-5.25-48.75-5.25-29.5 0-46.75 21t-17.25 51q0 22 8 38.375t20.25 25.75 30.5 15 35 7.375 37.25 1.75h42q20.5 0 37.25-1.75t35-7.375 30.5-15 20.25-25.75 8-38.375zM416 260q0 51.75-15.25 82.75-9.5 19.25-26.375 33.25t-35.25 21.5-42.5 11.875-42.875 5.5-41.75 1.125q-19.5 0-35.5-0.75t-36.875-3.125-38.125-7.5-34.25-12.875-30.25-20.25-21.5-28.75q-15.5-30.75-15.5-82.75 0-59.25 34-99-6.75-20.5-6.75-42.5 0-29 12.75-54.5 27 0 47.5 9.875t47.25 30.875q36.75-8.75 77.25-8.75 37 0 70 8 26.25-20.5 46.75-30.25t47.25-9.75q12.75 25.5 12.75 54.5 0 21.75-6.75 42 34 40 34 99.5z\" fill=\"currentColor\"></path></svg>\n</defs></svg>\n\n    <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off>\n    <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off>\n    <label class=md-overlay data-md-component=overlay for=__drawer></label> <a href=\"#{{ page.title | slugify }}\" tabindex=1 class=md-skip> Skip to content </a>\n    <header class=md-header data-md-component=header>\n        <nav class=\"md-header-nav md-grid\">\n            <div class=md-flex>\n                <div class=\"md-flex__cell md-flex__cell--shrink\">\n                  <a class=\"md-header-nav__button md-logo\" href=\"{{ site.url }}\" title=\"{{ site.title }}\">\n                    {% if site.logo %}<img height=\"{{ site.logo_pixels }}\" src=\"{{ site.baseurl }}/{{ site.logo }}\" width=\"{{ site.logo_pixels }}\">{% else %}<i class=\"md-icon\" style=\"margin-top:5px\"></i>{% endif %}</a>\n                </div>\n                <div class=\"md-flex__cell md-flex__cell--shrink\">\n                    <label class=\"md-icon md-icon--menu md-header-nav__button\" for=__drawer></label>\n                </div>\n                <div class=\"md-flex__cell md-flex__cell--stretch\">\n                    <div class=\"md-flex__ellipsis md-header-nav__title\" data-md-component=title> \n                        <span class=md-header-nav__topic>{{ site.title }}</span>\n                        <span class=md-header-nav__topic>{{ page.title }}</span>\n                    </div>\n                </div>\n                <div class=\"md-flex__cell md-flex__cell--shrink\">\n                    <label class=\"md-icon md-icon--search md-header-nav__button\" for=__search></label>\n                    <div class=md-search data-md-component=search role=dialog>\n                        <label class=md-search__overlay for=__search></label>\n                        <div class=md-search__inner role=search>\n                            <form class=md-search__form name=search>\n                                <input type=text class=md-search__input name=query placeholder=Search autocapitalize=off autocorrect=off autocomplete=off spellcheck=false data-md-component=query data-md-state=active>\n                                <label class=\"md-icon md-search__icon\" for=__search></label>\n                                <button type=reset class=\"md-icon md-search__icon\" data-md-component=reset tabindex=-1> &#xE5CD; </button>\n                            </form>\n                            <div class=md-search__output>\n                                <div class=md-search__scrollwrap data-md-scrollfix>\n                                    <div class=md-search-result data-md-component=result>\n                                        <div class=md-search-result__meta> Type to start searching </div>\n                                        <ol class=md-search-result__list></ol>\n                                    </div>\n                                </div>\n                            </div>\n                        </div>\n                    </div>\n                </div>\n      <div class=\"md-flex__cell md-flex__cell--shrink\">\n        <div class=\"md-header-nav__source\">\n          <a class=\"md-source\" data-md-source=\"github\" href=\"{{ site.repo }}\" title=\"Go to repository\">\n          <div class=\"md-source__icon\" style=\"padding-top:5px\">\n            <i class=\"fa fa-github fa-3x\"></i>\n          </div>\n          <div class=\"md-source__repository\" style=\"min-width:150px\">\n            {{ site.github_user }}/{{ site.github_repo }}\n          </div></a>\n        </div>\n      </div>\n    </div>\n  </nav>\n</header>\n"
  },
  {
    "path": "docs/_includes/permalinks.html",
    "content": "<script>\nvar headers = [\"h1\", \"h2\", \"h3\", \"h4\"]\nvar colors = [\"red\", \"orange\", \"green\", \"blue\"]\n\n$.each(headers, function(i, header){\n    var color = colors[i];\n    $(header).each(function () {\n        var href=$(this).attr(\"id\");\n        // $(this).append('<a class=\"headerlink\" style=\"color:' + color + '\" href=\"#' + href + '\" title=\"Permanent link\">¶</a>')\n    });\n})\n</script>\n"
  },
  {
    "path": "docs/_includes/quiz/multiple-choice.html",
    "content": "<div class=\"admonition question\">\n<p class=\"admonition-title\">{% if include.item.question %}{{ include.item.question }}{% else %}Question {{ include.count }}{% endif %}</p>\n<p>{% for choice in include.item.items %}{% if choice.correct == true %}<span class=\"correct-{{ include.count }}\">{% endif %}{{ forloop.index }}. {{ choice.choice }}{% if choice.correct == true %}</span>{% endif %}<br>{% endfor %}</p>\n</div><button class=\"btn btn-success\" onclick='$(\".correct-{{ include.count }}\").css(\"font-weight\", 600);$(\".correct-more-{{ include.count }}\").css(\"display\", \"inline\")'>Show Answer</button>\n{% if include.item.followup %}<p class=\"well correct-more-{{ include.count }}\" style=\"display:none\">{{ include.item.followup }}</p>{% endif %}\n"
  },
  {
    "path": "docs/_includes/quiz.html",
    "content": "{% assign quiz = site.data.quizzes[include.file] %}\n{% if quiz.randomize == true %}{% assign questions = quiz.questions | sample %}{% else %}{% assign questions = quiz.questions %}{% endif %}{% if quiz.title %}<h2>{{ quiz.title }}</h2>{% endif %}{% for item in questions %}\n{% if item.type == \"multiple-choice\" %}{% include quiz/multiple-choice.html item=item count=forloop.index %}{% endif %}<br>{% endfor %}\n"
  },
  {
    "path": "docs/_includes/scripts.html",
    "content": "<script src=\"https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js\" integrity=\"sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49\" crossorigin=\"anonymous\"></script>\n<script src=\"https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js\" integrity=\"sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy\" crossorigin=\"anonymous\"></script>\n<script src=\"{{ site.baseurl }}/assets/js/main.js\"></script>\n\n<script src=\"https://cdn.jsdelivr.net/npm/mermaid@11.4.0/dist/mermaid.min.js\"></script>\n<script>\n    $(document).ready(function () {\n        mermaid.initialize({\n            startOnLoad:true,\n            theme: \"default\",\n        });\n        window.mermaid.init(undefined, document.querySelectorAll('.language-mermaid'));\n    });\n</script>"
  },
  {
    "path": "docs/_includes/scrolltop.html",
    "content": "<style>\n#scrolltop {\n  display: none; /* Hidden by default */\n  position: fixed; /* Fixed/sticky position */\n  bottom: 20px; /* Place the button at the bottom of the page */\n  right: 30px; /* Place the button 30px from the right */\n  z-index: 99; /* Make sure it does not overlap */\n  border: none; /* Remove borders */\n  outline: none; /* Remove outline */\n  background-color: {{ site.color }}; /* Set a background color */\n  color: white; /* Text color */\n  cursor: pointer; /* Add a mouse pointer on hover */\n  padding: 10px 15px; /* Some padding */\n  border-radius: 100px; /* Rounded corners */\n  font-size: 18px; /* Increase font size */\n  font-weight: 600;\n}\n\n#scrolltop:hover {\n  background-color: #555; /* Add a dark-grey background on hover */\n}\n</style>\n<button onclick=\"topFunction()\" id=\"scrolltop\" title=\"Go to top\">🔝</button>\n\n<script>\n// When the user scrolls down 20px from the top of the document, show the button\nwindow.onscroll = function() {scrollFunction()};\n\nfunction scrollFunction() {\n  if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) {\n    document.getElementById(\"scrolltop\").style.display = \"block\";\n  } else {\n    document.getElementById(\"scrolltop\").style.display = \"none\";\n  }\n}\n\n// When the user clicks on the button, scroll to the top of the document\nfunction topFunction() {\n  document.body.scrollTop = 0; // For Safari\n  document.documentElement.scrollTop = 0; // For Chrome, Firefox, IE and Opera\n}\n</script>\n"
  },
  {
    "path": "docs/_includes/sidebar.html",
    "content": "<div id=\"td-sidebar-menu\" class=\"td-sidebar__inner\">\n\n  <form class=\"td-sidebar__search d-flex align-items-center\">\n    <input type=\"search\" class=\"form-control td-search-input\" placeholder=\"Search…\" aria-label=\"Search this site…\" autocomplete=\"off\">\n      <button class=\"btn btn-link td-sidebar__toggle d-md-none p-0 ml-3 fas fa-bars\" type=\"button\" data-toggle=\"collapse\" data-target=\"#td-section-nav\" aria-controls=\"td-docs-nav\" aria-expanded=\"false\" aria-label=\"Toggle section navigation\">\n      </button>\n  </form>\n\n  <nav class=\"collapse td-sidebar-nav py-2 pl-4\" id=\"td-section-nav\">\n    {% for section in site.data.toc %}\n      <ul class=\"td-sidebar-nav__section pr-md-3\">\n        {% capture sectionUrl %}\n          {{ section.url | replace: \"/\", \"\" }}\n        {% endcapture %}\n        {% capture pageUrl %}\n          {{ page.url | replace: \"/\", \"\" }}\n        {% endcapture %}\n\n        <li class=\"td-sidebar-nav__section-title\">\n          <a  href=\"{% if section.url %}{{ site.baseurl }}/{{ section.url }}{% else %}{{ section.external_url }}{% endif %}\" class=\"align-left pl-0 pr-2 active td-sidebar-link td-sidebar-link__section\">{{ section.title }}</a>\n        </li>\n        {% if section.links %}\n          <ul>\n            <li class=\"collapse show\" id=\"{{ section.title | slugify }}\">\n              <ul class=\"td-sidebar-nav__section pr-md-3\">\n                {% for entry in section.links %}\n                  <li class=\"td-sidebar-nav__section-title\">\n                    <a href=\"{% if entry.url %}\n                                {{ site.baseurl }}/{{ entry.url }}\n                             {% else %}\n                                {{ entry.external_url }}\n                             {% endif %}\"\n                       class=\"align-left pl-0 pr-2 td-sidebar-link td-sidebar-link__section\">{{ entry.title }}\n                    </a>\n                  </li>\n                  {% if page.url contains entry.url or pageUrl == sectionUrl %}\n                    <ul>\n                      <li class=\"collapse show\" id=\"{{ child.title | slugify }}\">\n                        {% if entry.children %}\n                          {% for child in entry.children %}\n                            <a class=\"td-sidebar-link td-sidebar-link__page \" id=\"m-{{ section.title | slugify }}-{{ entry.title | slugify }}-{{ child.title | slugify }}\" href=\"{% if child.url %}{{ site.baseurl }}/{{ child.url }}{% else %}{{ child.external_url }}{% endif %}\">{{ child.title }}</a>{% endfor %}\n                        {% endif %}\n                      </li>\n                    </ul>\n                  {% endif %}\n                {% endfor %}\n              </ul>\n            </li>\n          </ul>\n        {% endif %}\n      </ul>\n    {% endfor %}\n  </nav>\n</div>\n"
  },
  {
    "path": "docs/_includes/tags.html",
    "content": "{% if page.tags %}<script>\n$('h1').first().append('<div>{% for tag in page.tags %}<span style=\"font-size:12px\" class=\"badge badge-{{ site.tag_color }}\"><a style=\"cursor:pointer; color:white\" href=\"{% if site.tag_search_endpoint %}{{ site.tag_search_endpoint }}{{ tag }}{% else %}{{ site.url }}{{ site.baseurl }}/tags#{{ tag }} {% endif %}\">{{ tag }}</a></span>{% endfor %}</div>')</script>{% endif %}\n"
  },
  {
    "path": "docs/_includes/toc.html",
    "content": "{% capture tocWorkspace %}\n    {% comment %}\n        Copyright (c) 2017 Vladimir \"allejo\" Jimenez\n\n        Permission is hereby granted, free of charge, to any person\n        obtaining a copy of this software and associated documentation\n        files (the \"Software\"), to deal in the Software without\n        restriction, including without limitation the rights to use,\n        copy, modify, merge, publish, distribute, sublicense, and/or sell\n        copies of the Software, and to permit persons to whom the\n        Software is furnished to do so, subject to the following\n        conditions:\n\n        The above copyright notice and this permission notice shall be\n        included in all copies or substantial portions of the Software.\n\n        THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n        EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\n        OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n        NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n        HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n        WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n        FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n        OTHER DEALINGS IN THE SOFTWARE.\n    {% endcomment %}\n    {% comment %}\n        Version 1.2.1\n          https://github.com/allejo/jekyll-toc\n\n        \"...like all things liquid - where there's a will, and ~36 hours to spare, there's usually a/some way\" ~jaybe\n\n        Usage:\n            {% include toc.html html=content sanitize=true class=\"inline_toc\" id=\"my_toc\" h_min=2 h_max=3 %}\n\n        Parameters:\n            * html         (string) - the HTML of compiled markdown generated by kramdown in Jekyll\n\n        Optional Parameters:\n            * sanitize      (bool)   : false  - when set to true, the headers will be stripped of any HTML in the TOC\n            * class         (string) :   ''   - a CSS class assigned to the TOC\n            * id            (string) :   ''   - an ID to assigned to the TOC\n            * h_min         (int)    :   1    - the minimum TOC header level to use; any header lower than this value will be ignored\n            * h_max         (int)    :   6    - the maximum TOC header level to use; any header greater than this value will be ignored\n            * ordered       (bool)   : false  - when set to true, an ordered list will be outputted instead of an unordered list\n            * item_class    (string) :   ''   - add custom class(es) for each list item; has support for '%level%' placeholder, which is the current heading level\n            * submenu_class (string) :   ''   - add custom class(es) for each child group of headings; has support for '%level%' placeholder which is the current \"submenu\" heading level\n            * base_url      (string) :   ''   - add a base url to the TOC links for when your TOC is on another page than the actual content\n            * anchor_class  (string) :   ''   - add custom class(es) for each anchor element\n            * skip_no_ids   (bool)   : false  - skip headers that do not have an `id` attribute\n            * flat_toc      (bool)   : false  - when set to true, the TOC will be a single level list\n\n        Output:\n            An ordered or unordered list representing the table of contents of a markdown block. This snippet will only\n            generate the table of contents and will NOT output the markdown given to it\n    {% endcomment %}\n\n    {% capture newline %}\n    {% endcapture %}\n    {% assign newline = newline | rstrip %} <!-- Remove the extra spacing but preserve the newline -->\n\n    {% capture deprecation_warnings %}{% endcapture %}\n\n    {% if include.baseurl %}\n        {% capture deprecation_warnings %}{{ deprecation_warnings }}<!-- jekyll-toc :: \"baseurl\" has been deprecated, use \"base_url\" instead -->{{ newline }}{% endcapture %}\n    {% endif %}\n\n    {% if include.skipNoIDs %}\n        {% capture deprecation_warnings %}{{ deprecation_warnings }}<!-- jekyll-toc :: \"skipNoIDs\" has been deprecated, use \"skip_no_ids\" instead -->{{ newline }}{% endcapture %}\n    {% endif %}\n\n    {% capture jekyll_toc %}{% endcapture %}\n    {% assign orderedList = include.ordered | default: false %}\n    {% assign flatToc = include.flat_toc | default: false %}\n    {% assign baseURL = include.base_url | default: include.baseurl | default: '' %}\n    {% assign skipNoIDs = include.skip_no_ids | default: include.skipNoIDs | default: false %}\n    {% assign minHeader = include.h_min | default: 1 %}\n    {% assign maxHeader = include.h_max | default: 6 %}\n    {% assign nodes = include.html | strip | split: '<h' %}\n\n    {% assign firstHeader = true %}\n    {% assign currLevel = 0 %}\n    {% assign lastLevel = 0 %}\n\n    {% capture listModifier %}{% if orderedList %}ol{% else %}ul{% endif %}{% endcapture %}\n\n    {% for node in nodes %}\n        {% if node == \"\" %}\n            {% continue %}\n        {% endif %}\n\n        {% assign currLevel = node | replace: '\"', '' | slice: 0, 1 | times: 1 %}\n\n        {% if currLevel < minHeader or currLevel > maxHeader %}\n            {% continue %}\n        {% endif %}\n\n        {% assign _workspace = node | split: '</h' %}\n\n        {% assign _idWorkspace = _workspace[0] | split: 'id=\"' %}\n        {% assign _idWorkspace = _idWorkspace[1] | split: '\"' %}\n        {% assign htmlID = _idWorkspace[0] %}\n\n        {% assign _classWorkspace = _workspace[0] | split: 'class=\"' %}\n        {% assign _classWorkspace = _classWorkspace[1] | split: '\"' %}\n        {% assign htmlClass = _classWorkspace[0] %}\n\n        {% if htmlClass contains \"no_toc\" %}\n            {% continue %}\n        {% endif %}\n\n        {% if firstHeader %}\n            {% assign minHeader = currLevel %}\n        {% endif %}\n\n        {% capture _hAttrToStrip %}{{ _workspace[0] | split: '>' | first }}>{% endcapture %}\n        {% assign header = _workspace[0] | replace: _hAttrToStrip, '' %}\n\n        {% if include.item_class and include.item_class != blank %}\n            {% capture listItemClass %} class=\"{{ include.item_class | replace: '%level%', currLevel | split: '.' | join: ' ' }}\"{% endcapture %}\n        {% endif %}\n\n        {% if include.submenu_class and include.submenu_class != blank %}\n            {% assign subMenuLevel = currLevel | minus: 1 %}\n            {% capture subMenuClass %} class=\"{{ include.submenu_class | replace: '%level%', subMenuLevel | split: '.' | join: ' ' }}\"{% endcapture %}\n        {% endif %}\n\n        {% capture anchorBody %}{% if include.sanitize %}{{ header | strip_html }}{% else %}{{ header }}{% endif %}{% endcapture %}\n\n        {% if htmlID %}\n            {% capture anchorAttributes %} href=\"{% if baseURL %}{{ baseURL }}{% endif %}#{{ htmlID }}\"{% endcapture %}\n\n            {% if include.anchor_class %}\n                {% capture anchorAttributes %}{{ anchorAttributes }} class=\"{{ include.anchor_class | split: '.' | join: ' ' }}\"{% endcapture %}\n            {% endif %}\n\n            {% capture listItem %}<a{{ anchorAttributes }}>{{ anchorBody }}</a>{% endcapture %}\n        {% elsif skipNoIDs == true %}\n            {% continue %}\n        {% else %}\n            {% capture listItem %}{{ anchorBody }}{% endcapture %}\n        {% endif %}\n\n        {% if currLevel > lastLevel and flatToc == false %}\n            {% capture jekyll_toc %}{{ jekyll_toc }}<{{ listModifier }}{{ subMenuClass }}>{% endcapture %}\n        {% elsif currLevel < lastLevel and flatToc == false %}\n            {% assign repeatCount = lastLevel | minus: currLevel %}\n\n            {% for i in (1..repeatCount) %}\n                {% capture jekyll_toc %}{{ jekyll_toc }}</li></{{ listModifier }}>{% endcapture %}\n            {% endfor %}\n\n            {% capture jekyll_toc %}{{ jekyll_toc }}</li>{% endcapture %}\n        {% else %}\n            {% capture jekyll_toc %}{{ jekyll_toc }}</li>{% endcapture %}\n        {% endif %}\n\n        {% capture jekyll_toc %}{{ jekyll_toc }}<li{{ listItemClass }}>{{ listItem }}{% endcapture %}\n\n        {% assign lastLevel = currLevel %}\n        {% assign firstHeader = false %}\n    {% endfor %}\n\n    {% if flatToc == true %}\n        {% assign repeatCount = 1 %}\n    {% else %}\n        {% assign repeatCount = minHeader | minus: 1 %}\n        {% assign repeatCount = lastLevel | minus: repeatCount %}\n    {% endif %}\n\n    {% for i in (1..repeatCount) %}\n        {% capture jekyll_toc %}{{ jekyll_toc }}</li></{{ listModifier }}>{% endcapture %}\n    {% endfor %}\n\n    {% if jekyll_toc != '' %}\n        {% assign rootAttributes = '' %}\n        {% if include.class and include.class != blank %}\n            {% capture rootAttributes %} class=\"{{ include.class | split: '.' | join: ' ' }}\"{% endcapture %}\n        {% endif %}\n\n        {% if include.id and include.id != blank %}\n            {% capture rootAttributes %}{{ rootAttributes }} id=\"{{ include.id }}\"{% endcapture %}\n        {% endif %}\n\n        {% if rootAttributes %}\n            {% assign nodes = jekyll_toc | split: '>' %}\n            {% capture jekyll_toc %}<{{ listModifier }}{{ rootAttributes }}>{{ nodes | shift | join: '>' }}>{% endcapture %}\n        {% endif %}\n    {% endif %}\n{% endcapture %}{% assign tocWorkspace = '' %}{{ deprecation_warnings }}{{ jekyll_toc -}}\n"
  },
  {
    "path": "docs/_includes/video.html",
    "content": "﻿<video autoplay muted loop>\n    <source src=\"{{site.baseurl}}/{{include.path}}\" type=\"video/mp4\">\n    Your browser does not support the video tag.\n</video>"
  },
  {
    "path": "docs/_layouts/default.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\" class=\"no-js\">\n  <!-- Copyright 2019-2021 Vanessa Sochat-->\n  {% include head.html %}\n  {% include google-analytics.html %}\n  <body class=\"td-section\">\n    {% include header.html %}\n    <div class=\"container-fluid td-outer\">\n      <div class=\"td-main\">\n        <div class=\"row flex-xl-nowrap\">\n          <div class=\"col-12 col-md-3 col-xl-2 td-sidebar py-0 d-print-none\">\n          {% include sidebar.html %}\n          </div>\n          <main class=\"col-12 col-md-9 col-xl-10 pl-md-5 pt-1 pt-md-5 main-column\" role=\"main\">\n           <div class=\" pt-0 pt-md-5\">\n\t      {{ content }}\n              {% if section.links %}<div class=\"section-index\">\n               <hr class=\"panel-line\">{% for child in section.links %}\n                 <div class=\"entry\">\n                    <h5>\n                        <a href=\"{% if child.url %}{{ site.baseurl }}{{ child.url }}{% else %}{{ child.external_url }}{% endif %}\">{{ child.title }}</a>\n                    </h5>\n                    <p>{{ child.description }}</p>\n                 </div>{% endfor %}\n              </div>{% endif %}\n              {% include feedback.html %}\n           </div>\n          </main>\n        </div>\n      </div>\n      {% include footer.html %}\n    </div>\n    {% include scripts.html %}\n  </body>\n</html>\n"
  },
  {
    "path": "docs/_layouts/page.html",
    "content": "---\nlayout: default\n---\n{% unless page.no_toc  %}\n<h1>{{ page.title }}</h1>\n<h2>Table of contents</h2>\n{% include toc.html html=content %}\n{% endunless %}\n{{ content }}\n{% include permalinks.html %}\n{% include tags.html %}\n"
  },
  {
    "path": "docs/_layouts/post.html",
    "content": "---\nlayout: page\n---\n<h1 style=\"margin-bottom:0px\">{{ page.title }}</h1>\n{% if page.badges %}{% for badge in page.badges %}<span class=\"badge badge-{{ badge.type }}\">{{ badge.tag }}</span>{% endfor %}{% endif %}\n<span class=\"post-date\" style=\"font-style: italic;\">{{ page.date | date: \"%B %d, %Y\" }}</span>\n{{ content }}\n"
  },
  {
    "path": "docs/_posts/.gitkeep",
    "content": ""
  },
  {
    "path": "docs/_recipes/01_driving_a_progress_bar.md",
    "content": "---\nlayout: page\ntitle: Driving a progress bar\npermalink: /usage/recipes/driving-a-progress-bar\ndescription: \"How to use action timings to drive a progress bar.\"\n---\n\n## Introduction\n\nWhen using a controller as an input device, very often we have more actions than we have buttons on the controller. A common way to work around this problem is to map one action to a long press of a button instead of a short press. For example a quick press of the _A_ button could be used to jump, while a long press of the _A_ button could be used to do a big jump. \n\nThis is a great way to make use of the limited number of buttons on a controller. However, the player needs to know how long they have to hold the button down to trigger the action. Something that many games do is to show a progress bar that fills up while the button is pressed. So how can we do this?\n\n## Setup\n\nFirst we need some actions. For this let's create an action named _Jump_ and another one named _Somersault_. Now we can map these actions in a mapping context like this:\n\n![A tap and hold mapping configuration]({{ site.baseurl }}/assets/img/recipes/tap_and_hold_mc.png)\n\nThe _Jump_ action uses a _Tap_ trigger, so it will only fire when the player taps the button for less than 0.2 seconds. \n\n\n![The tap trigger for jumping]({{ site.baseurl }}/assets/img/recipes/jump_tap_trigger.png)\n\nThe _Somersault_ action uses a _Hold_ trigger, so it will only fire when the player holds the button for a full second. The hold trigger is also set to _One Shot_ mode, so it will only fire once when the button is pressed.\n\n![The hold trigger for the somersault]({{ site.baseurl }}/assets/img/recipes/somersault_hold_trigger.png)\n\nBy setting things up this way, we make sure that the actions are mutually exclusive. The player can either jump or do a somersault, but not both at the same time.\n\n## Triggering the actions\n\nFor simplicity we'll just wire our two actions to an animation. We have a jump animation and a somersault animation, and whenever an action is triggered, we play the corresponding animation. \n\n{% include video.html path=\"assets/img/recipes/jump_somersault_animations.mp4\" %}\n\nNow we can use the `triggered` signal on our actions to tell an animation player to play the corresponding animation.\n\n```gdscript\n# We get the actions as export variables so we can assign them in the editor.\n@export var jump_action:GUIDEAction\n@export var somersault_action:GUIDEAction\n\n# And we get the animation player as an onready variable so we can use it in our code.\n@onready var _animation_player:AnimationPlayer = $AnimationPlayer\n\nfunc _ready(): \n    # Connect the triggered signal of the actions to our play function.\n    jump_action.triggered.connect(_play.bind(\"jump\"))\n    somersault_action.triggered.connect(_play.bind(\"somersault\"))\n\nfunc _play(animation:String):\n    # Play the animation on the animation player.\n    _animation_player.play(animation)\n\n```\n\n## Driving the progress bar\nNow that is simple enough, but how do we show to our player how long they have to hold the button down? Let's use a simple progress bar to our scene to show this:\n\n![The progress bar node in the scene tree]({{ site.baseurl }}/assets/img/recipes/progress_bar_node.png)\n\nWe can add an `@onready` variable to our script to get a reference to the progress bar node.\n\n```gdscript\n@onready var _progress_bar:ProgressBar = $ProgressBar\n```\n\nNow to actually drive the progress bar, we can use some properties of the action. Every `GUIDEAction` has some properties that give us timing information about the action. These properties are:\n\n- `elapsed_seconds` - This starts at 0 and counts up while the action is in `ONGOING` state. So if our action is bound to a _Hold_ trigger, this will count up while the button is pressed but the action was not yet triggered. \n- `triggered_seconds` - This starts at 0 and counts up while the action is in `TRIGGERED` state.\n\nNow `elapsed_seconds` is useful to show the progress bar but we need to know when the action is in `ONGOING` state. Fortunately `GUIDEAction` also has a signal for this. So we can connect to this in our ready function and update the progress bar accordingly. We also want to hide the progress bar when then action is triggered or cancelled, so we connect to the `triggered` and `cancelled` signals as well.\n\n```gdscript\nfunc _ready():\n    ...\n    somersault_action.ongoing.connect(_update_progress_bar)\n    somersault_action.triggered.connect(_hide_progress_bar)\n    somersault_action.cancelled.connect(_hide_progress_bar)\n\nfunc _update_progress_bar():\n    _progress_bar.value = somersault_action.elapsed_seconds\n    _progress_bar.visible = true\n\nfunc _hide_progress_bar():\n    _progress_bar.visible = false\n```\n\nNow there is one little problem with this. By default the progress bar has a range of 0-100. So if we play this, our progress bar will only show a little bit of progress, because the `elapsed_seconds` property is in seconds. \n\n{% include video.html path=\"assets/img/recipes/somersault_broken_progress_bar.mp4\" %}\n\nOne way to fix this is to set the range of the progress bar to 0-1 instead of 0-100. This way the progress bar will fill up completely when the action is triggered. \n\n![The progress bar range set to 0-1]({{ site.baseurl }}/assets/img/recipes/progress_bar_range.png)\n\nThis is better but it's still not great because now we need to update the progress bar's range every time we change the action's hold time. Things like the hold time are usually changed quite often during playtesting to make the game feel just right. So we want to avoid having to change the progress bar's range every time we do this. \n\nWe can fix this by using another property of the action:\n\n- `elapsed_ratio` - This is a value between 0 and 1 that represents the ratio of the elapsed time to the hold time. So if the action has a hold time of 1 second and the elapsed time is 0.5 seconds, this will be 0.5. \n\nThis is great for driving progress indicators as the range is always 0-1 no matter how long the hold time is. So all we need to do is set the progress bar's value to the `elapsed_ratio` property of the action. \n\n```gdscript\nfunc _update_progress_bar():\n    _progress_bar.value = somersault_action.elapsed_ratio\n    ...\n```\n\nAnd now our progress bar works nicely! \n\n{% include video.html path=\"assets/img/recipes/somersault_final_progress_bar.mp4\" %}\n\n## Conclusion\n\nIn this recipe we learned how to use the `elapsed_seconds` and `elapsed_ratio` properties of a `GUIDEAction` to drive a progress bar. We also learned how to use the `ongoing`, `triggered` and `cancelled` signals to show and hide the progress bar. If you'd like to see this in action, you can check out the `tap_and_hold` example that inside of the `guide_examples` folder."
  },
  {
    "path": "docs/_recipes/02_showing_debug_info_with_custom_ui.md",
    "content": "---\nlayout: page\ntitle: Using a custom UI to show debug info\npermalink: /usage/recipes/custom-debug-ui\ndescription: \"How to write a custom UI to show debug info about the current state of G.U.I.D.E.\"\n---\n\n## Introduction\n\nG.U.I.D.E ships with a [built-in debugger](https://github.com/godotneers/G.U.I.D.E/blob/main/addons/guide/debugger/guide_debugger.gd), but you may not like its layout, or it may not fit into your own debugging interface that you use for the rest of the game. So this recipe gives you a starting point on how to write your own debugging UI.\n\n### Accessing G.U.I.D.E data for your custom UI\n\nWhen driving a custom debug panel, you mainly need three kinds of information from G.U.I.D.E at runtime:\n\n- Which action mappings are currently active and what their state/value is.\n- Which inputs are currently active and what their raw values are.\n- Where multiple actions share the same input and how the priority shakes out.\n\nThe built-in debugger shows all of this by reading the live state from the `GUIDE` autoload. The snippets below focus on how to access the data so you can render it in any UI you like.\n\n> **Note**: Members starting with an underscore are internal API. They are not considered stable and may change without warning in future versions. Prefer public properties and signals where available. If you decide to use internal members for debugging UIs, keep this warning in mind and Check if your UI still works after you update G.U.I.D.E.\n\n#### Refreshing when mappings change\n\n- Listen to `GUIDE.input_mappings_changed` to refresh anything that depends on the currently active mapping contexts (e.g. priority/overlap lists). This signal fires whenever active contexts are re-evaluated.\n\n```gdscript\nfunc _ready() -> void:\n    GUIDE.input_mappings_changed.connect(_rebuild_from_contexts)\n```\n\nIf your debug UI should tick even while hidden or while the scene is paused, consider setting process_mode to `Node.PROCESS_MODE_ALWAYS` on your panel script and guard your per-frame work with `is_visible_in_tree()`.\n\n#### Enumerating active action mappings\n\n- `GUIDE._active_action_mappings` contains the active `GUIDEActionMapping` entries in their current priority order.\n- Each mapping has an `action: GUIDEAction` you can inspect.\n- You can get a human readable name of the action with: `action._editor_name()`.\n- To present the current state: use either the convenience methods (`action.is_triggered()`, `action.is_ongoing()`) or read the last state from the internal enum `action._last_state` (values: `TRIGGERED`, `ONGOING`, `COMPLETED`).\n- To present the current value: choose the value property that matches the action’s configured value type: `value_bool`, `value_axis_1d`, `value_axis_2d` or `value_axis_3d`.\n\nExample access pattern for listing active actions, their state and value:\n\n```gdscript\nfor mapping:GUIDEActionMapping in GUIDE._active_action_mappings:\n    var action:GUIDEAction = mapping.action\n\n    var name := action._editor_name()\n\n    var state_text := \"\"\n    if action.is_triggered():\n        state_text = \"Triggered\"\n    elif action.is_ongoing():\n        state_text = \"Ongoing\"\n    elif action.was_completed():\n        state_text = \"Completed\"\n\n    var value_text := \"\"\n    match action.action_value_type:\n        GUIDEAction.GUIDEActionValueType.BOOL:\n            value_text = str(action.value_bool)\n        GUIDEAction.GUIDEActionValueType.AXIS_1D:\n            value_text = str(action.value_axis_1d)\n        GUIDEAction.GUIDEActionValueType.AXIS_2D:\n            value_text = str(action.value_axis_2d)\n        GUIDEAction.GUIDEActionValueType.AXIS_3D:\n            value_text = str(action.value_axis_3d)\n\n    # Use name, state_text, value_text to drive your UI.\n```\n#### Enumerating currently active inputs\n\nG.U.I.D.E tracks inputs that are currently contributing to action values:\n\n- `GUIDE._active_inputs` is a dictionary of live input instances; iterate its `.values()` to access them.\n- Each input has an internal numeric vector value at `input._value` (`Vector3`). This is the raw, aggregated value after modifiers and triggers have been applied in the active contexts.\n- To get a human-readable label for an input instance that respects the active contexts and their configuration, use a formatter:\n  - Create one with `GUIDEInputFormatter.for_active_contexts()`.\n  - Call `formatter.input_as_text(input, false)` to get a concise label.\n\nExample access pattern for listing active inputs and their current values:\n\n```gdscript\nvar formatter:GUIDEInputFormatter = GUIDEInputFormatter.for_active_contexts()\nfor input in GUIDE._active_inputs.values():\n    var label := formatter.input_as_text(input, false)\n    var value := input._value  # Vector3 (internal)\n    # Use label and value to drive your UI.\n```\n#### Inspecting input overlaps and priorities\n\nWhen multiple actions share the same input, G.U.I.D.E will resolve which action wins based on priority and blocking rules. To present overlap information:\n\n- `GUIDE._actions_sharing_input` maps a `GUIDEAction` to a list of other `GUIDEAction` instances that currently share the same effective input.\n- Rebuild any overlap/priority lists when `GUIDE.input_mappings_changed` fires.\n\nExample access pattern for building a simple overlap list:\n\n```gdscript\nfunc _rebuild_from_contexts() -> void:\n    for mapping:GUIDEActionMapping in GUIDE._active_action_mappings:\n        var action := mapping.action\n        if GUIDE._actions_sharing_input.has(action):\n            var others:Array = GUIDE._actions_sharing_input[action]\n            var names := \", \".join(others.map(func(a): return a._editor_name()))\n            var summary := \"[\" + action._editor_name() + \"] > [\" + names + \"]\"\n            # Use summary in your UI.\n```\n\nThis mirrors what the shipped debugger displays in its “Priorities” section.\n\n## Example\n\n[Laurent Senta](https://gist.github.com/laurentsenta) has created a custom debug panel that uses IMGUI to display the current input state. \n\n![G.U.I.D.E debug information in IMGUI]({{ site.baseurl }}/assets/img/recipes/imgui_example.png)\n\nYou can find the code for this on [GitHub](https://gist.github.com/laurentsenta/5e7c5539c22d4687d7ee9330069710f8).\n\n"
  },
  {
    "path": "docs/assets/css/main.css",
    "content": "---\nlayout: null\nexcluded_in_search: true\n---\n\n@import \"https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,700,700i\";\n\nh1 + h2 {\n    margin-top: 1.5rem;\n}\n\nh2 {\n    font-size: 1.3rem !important;\n}\n\nh3 {\n    font-size: 1.1rem !important;\n}\n\nh4 {\n    font-size: 1rem !important;\n}\n\n.state-icon {\n    height: 1.3rem !important;\n    width: 1.3rem !important;\n    margin-right: 0.2rem;\n}\n\n.native-width {\n    width: unset !important;\n}\n\npre {\n    padding: 0.8rem;\n}\n\n.centered {\n    display: block;\n    margin-left: auto;\n    margin-right: auto;\n}\n\nblockquote {\n    padding-left: 1em;\n    border-left: 3px solid #ccc;\n    font-style: italic;\n}\n\ntd, th {\n    padding-left: 0.5rem;\n    padding-right: 0.5rem;\n}\n\n/* odd/even rows */\ntr:nth-child(even) {\n    background-color: #f2f2f2;\n}\n\ntable {\n    width: 100%;\n    border-collapse: collapse;\n    margin-top: 1rem;\n    margin-bottom: 2rem;\n}\n\n\n\n/* ----------------------------------------------------------------------------------------------- */\n\n.gh-source {\n    display: block;\n    padding-right: 1.2rem;\n    transition: opacity .25s;\n    font-size: 0.8rem;\n    line-height: 1.2;\n    color: white;\n    white-space: nowrap;\n}\n[dir=rtl] .gh-source {\n    padding-right: 0;\n    padding-left: 1.2rem;\n}\n.gh-source:hover {\n    color: yellow;\n}\n.gh-source__icon+.gh-source__repository {\n    margin-left: -4.4rem;\n    padding-left: 4rem;\n}\n[dir=rtl] .gh-source__icon+.gh-source__repository {\n    margin-right: -4.4rem;\n    margin-left: 0;\n    padding-right: 4rem;\n    padding-left: 0;\n}\n.gh-source__repository {\n    display: inline-block;\n    max-width: 100%;\n    margin-left: 1.2rem;\n    font-weight: 700;\n    text-overflow: ellipsis;\n    overflow: hidden;\n    vertical-align: middle;\n}\n.gh-source__facts {\n    margin: 0;\n    padding: 0;\n    font-size: 0.8rem;\n    font-weight: 700;\n    list-style-type: none;\n    opacity: .75;\n    overflow: hidden;\n}\n[data-md-state=done] .gh-source__facts {\n    -webkit-animation: md-source__facts--done .25s ease-in;\n    animation: md-source__facts--done .25s ease-in;\n}\n.gh-source__fact {\n    float: left;\n}\n[dir=rtl] .gh-source__fact {\n    float: right;\n}\n[data-md-state=done] .gh-source__fact {\n    -webkit-animation: md-source__fact--done .4s ease-out;\n    animation: md-source__fact--done .4s ease-out;\n}\n.gh-source__fact:before {\n    margin: 0 .2rem;\n    content: \"\\B7\"}\n.gh-source__fact:first-child:before {\n    display: none;\n}\n.gh-source-file {\n    display: inline-block;\n    margin: 1em .5em 1em 0;\n    padding-right: .5rem;\n    border-radius: .2rem;\n    background-color: rgba(0, 0, 0, .07);\n    font-size: 1.28rem;\n    list-style-type: none;\n    cursor: pointer;\n    overflow: hidden;\n}\n.gh-source-file:before {\n    display: inline-block;\n    margin-right: .5rem;\n    padding: .5rem;\n    background-color: rgba(0, 0, 0, .26);\n    color: #fff;\n    font-size: 1.6rem;\n    content: \"\\E86F\";\n    vertical-align: middle;\n}\n\n\n.td-sidebar-nav {\n    padding-right: .5rem;\n    margin-right: -15px;\n    margin-left: -15px\n}\n\n@media(min-width:768px) {\n    @supports((position: -webkit-sticky) or (position: sticky)) {\n        .td-sidebar-nav {\n            max-height: -webkit-calc(100vh - 10rem);\n            max-height: calc(100vh - 10rem);\n            overflow-y: auto\n        }\n    }\n}\n\n@media(min-width:768px) {\n    .td-sidebar-nav {\n        display: block!important\n    }\n}\n\n.td-sidebar-nav__section {\n    padding-left: 0;\n    margin-bottom: 0px;\n}\n\n.td-sidebar-nav__section li {\n    list-style: none\n}\n\n.td-sidebar-nav__section ul {\n    padding: 0;\n    margin: 0\n}\n\n.td-sidebar-nav__section>ul {\n    padding-left: .5rem\n}\n\n.td-sidebar-nav__section-title {\n    display: block;\n    font-weight: 500\n}\n\n.td-sidebar-nav__section-title .active {\n    font-weight: 700\n}\n\n.td-sidebar-nav__section-title a {\n    color: #222\n}\n\n.td-sidebar-nav .td-sidebar-link {\n    display: block;\n    padding-bottom: .375rem\n}\n\n.td-sidebar-nav .td-sidebar-link__page {\n    color: #495057;\n    font-weight: 300\n}\n\n.td-sidebar-nav a:hover {\n    color: #72a1e5;\n    text-decoration: none\n}\n\n.td-sidebar-nav a.active {\n    font-weight: 700\n}\n\n.td-sidebar-nav .dropdown a {\n    color: #495057\n}\n\n.td-sidebar-nav .dropdown .nav-link {\n    padding: 0 0 1rem\n}\n\n.td-sidebar {\n    padding-bottom: 1rem\n}\n\n@media(min-width:768px) {\n    .td-sidebar {\n        padding-top: 4rem;\n        background-color: rgba(48, 99, 142, .03);\n        padding-right: 1rem;\n        border-right: 1px solid #dee2e6\n    }\n}\n\n.td-sidebar__toggle {\n    line-height: 1;\n    color: #222;\n    margin: 1rem\n}\n\n.td-sidebar__search {\n    padding: 1rem 15px;\n    margin-right: -15px;\n    margin-left: -15px\n}\n\n.td-sidebar__inner {\n    -webkit-box-ordinal-group: 1;\n    -webkit-order: 0;\n    -ms-flex-order: 0;\n    order: 0\n}\n\n@media(min-width:768px) {\n    @supports((position: -webkit-sticky) or (position: sticky)) {\n        .td-sidebar__inner {\n            position: -webkit-sticky;\n            position: sticky;\n            top: 4rem;\n            z-index: 10;\n            height: -webkit-calc(100vh - 6rem);\n            height: calc(100vh - 6rem)\n        }\n    }\n}\n\n@media(min-width:1200px) {\n    .td-sidebar__inner {\n        -webkit-box-flex: 0;\n        -webkit-flex: 0 1 320px;\n        -ms-flex: 0 1 320px;\n        flex: 0 1 320px\n    }\n}\n\n.td-sidebar__inner .td-search-box {\n    width: 100%\n}\n\n.td-toc {\n    border-left: 1px solid #dee2e6;\n    -webkit-box-ordinal-group: 3;\n    -webkit-order: 2;\n    -ms-flex-order: 2;\n    order: 2;\n    padding-top: .75rem;\n    padding-bottom: 1.5rem;\n    vertical-align: top\n}\n\n@supports((position:-webkit-sticky) or (position:sticky)) {\n    .td-toc {\n        position: -webkit-sticky;\n        position: sticky;\n        top: 4rem;\n        height: -webkit-calc(100vh - 10rem);\n        height: calc(100vh - 10rem);\n        overflow-y: auto\n    }\n}\n\n.td-toc a {\n    display: block;\n    font-weight: 300;\n    padding-bottom: .25rem\n}\n\n.td-toc li {\n    list-style: none;\n    display: block\n}\n\n.td-toc li li {\n    margin-left: .5rem\n}\n\n.td-toc .td-page-meta a {\n    font-weight: 500\n}\n\n.td-toc #TableOfContents a {\n    color: #888\n}\n\n.td-toc #TableOfContents a:hover {\n    color: #72a1e5;\n    text-decoration: none\n}\n\n.td-toc ul {\n    padding-left: 0\n}\n\n.btn {\n    border-radius: 1rem\n}\n\n.btn-lg,\n.btn-group-lg>.btn {\n    border-radius: 2rem\n}\n\n.btn-sm,\n.btn-group-sm>.btn {\n    border-radius: 1rem\n}\n\n.breadcrumb {\n    background: 0 0;\n    padding-left: 0;\n    padding-top: 0\n}\n\n.alert {\n    font-weight: 500;\n    background: #fff;\n    color: inherit;\n    border-radius: 0\n}\n\n.alert-primary {\n    border-style: solid;\n    border-color: {{ site.color }};\n    border-width: 0 0 0 4px\n}\n\n.alert-primary .alert-heading {\n    color: {{ site.color }}\n}\n\n.alert-secondary {\n    border-style: solid;\n    border-color: #888;\n    border-width: 0 0 0 4px\n}\n\n.alert-secondary .alert-heading {\n    color: #888\n}\n\n.alert-success {\n    border-style: solid;\n    border-color: #13a733;\n    border-width: 0 0 0 4px\n}\n\n.alert-success .alert-heading {\n    color: #13a733;\n}\n\n.alert-info {\n    border-style: solid;\n    border-color: #479de7;\n    border-width: 0 0 0 4px\n}\n\n.alert-info .alert-heading {\n    color: #479de7;\n}\n\n.alert-warning {\n    border-style: solid;\n    border-color: #f7820a;\n    border-width: 0 0 0 4px\n}\n\n.alert-warning .alert-heading {\n    color: #f7820a\n}\n\n.alert-danger {\n    border-style: solid;\n    border-color: #d95040;\n    border-width: 0 0 0 4px\n}\n\n.alert-danger .alert-heading {\n    color: #d95040\n}\n\n.alert-light {\n    border-style: solid;\n    border-color: #d3f3ee;\n    border-width: 0 0 0 4px\n}\n\n.alert-light .alert-heading {\n    color: #d3f3ee\n}\n\n.alert-dark {\n    border-style: solid;\n    border-color: #403f4c;\n    border-width: 0 0 0 4px\n}\n\n.alert-dark .alert-heading {\n    color: #403f4c\n}\n\n.td-content {\n    -webkit-box-ordinal-group: 2;\n    -webkit-order: 1;\n    -ms-flex-order: 1;\n    order: 1\n}\n\n.td-content p,\n.td-content li,\n.td-content td {\n    font-weight: 400\n}\n\n.td-content>h1 {\n    font-weight: 700;\n    margin-bottom: 1rem\n}\n\n.td-content>h2 {\n    margin-bottom: 1rem\n}\n\n.td-content>h2:not(:first-child) {\n    margin-top: 2.5rem\n}\n\n.td-content>h2+h3 {\n    margin-top: 1rem\n}\n\n.td-content>h3,\n.td-content>h4,\n.td-content>h5,\n.td-content>h6 {\n    margin-bottom: 1rem;\n    margin-top: 2rem\n}\n\n.td-content>blockquote {\n    padding: 0 0 0 1rem;\n    margin-bottom: 1rem;\n    color: #888;\n    border-left: 6px solid {{ site.color }}\n}\n\n.td-content>ul li,\n.td-content>ol li {\n    margin-bottom: .25rem\n}\n\n.td-content strong {\n    font-weight: 700\n}\n\n.td-content .alert:not(:first-child) {\n    margin-top: 2rem;\n    margin-bottom: 2rem\n}\n\n.td-content .lead {\n    margin-bottom: 1.5rem\n}\n\n.td-title {\n    margin-top: 1rem;\n    margin-bottom: .5rem\n}\n\n@media(min-width:576px) {\n    .td-title {\n        font-size: 3rem\n    }\n}\n\n.td-search-input.form-control:focus {\n    border-color: #f5f8fb;\n    -webkit-box-shadow: 0 0 0 2px #82afd5;\n    box-shadow: 0 0 0 2px #82afd5\n}\n\n.td-outer {\n    display: -webkit-box;\n    display: -webkit-flex;\n    display: -ms-flexbox;\n    display: flex;\n    -webkit-box-orient: vertical;\n    -webkit-box-direction: normal;\n    -webkit-flex-direction: column;\n    -ms-flex-direction: column;\n    flex-direction: column;\n    height: 100vh\n}\n\n@media(min-width:768px) {\n    .td-default main section:first-of-type {\n        padding-top: 8rem\n    }\n}\n\n.td-main {\n    -webkit-box-flex: 1;\n    -webkit-flex-grow: 1;\n    -ms-flex-positive: 1;\n    flex-grow: 1\n}\n\n.td-main main {\n    padding-bottom: 2rem\n}\n\n@media(min-width:768px) {\n    .td-main main {\n        padding-top: 4.5rem\n    }\n}\n\n.td-cover-block--height-min {\n    min-height: 300px\n}\n\n.td-cover-block--height-med {\n    min-height: 400px\n}\n\n.td-cover-block--height-max {\n    min-height: 500px\n}\n\n.td-cover-block--height-full {\n    min-height: 100vh\n}\n\n@media(min-width:768px) {\n    .td-cover-block--height-min {\n        min-height: 450px\n    }\n    .td-cover-block--height-med {\n        min-height: 500px\n    }\n    .td-cover-block--height-max {\n        min-height: 650px\n    }\n}\n\n.td-cover-logo {\n    margin-right: .5em\n}\n\n.td-cover-block {\n    position: relative;\n    padding-top: 5rem;\n    padding-bottom: 5rem;\n    background-repeat: no-repeat;\n    background-position: 50% 0;\n    -webkit-background-size: cover;\n    background-size: cover\n}\n\n.td-bg-arrow-wrapper {\n    position: relative\n}\n\n.section-index .entry {\n    padding: .75rem\n}\n\n.section-index h5 {\n    margin-bottom: 0\n}\n\n.section-index h5 a {\n    font-weight: 700\n}\n\n.section-index p {\n    margin-top: 0\n}\n\n.pageinfo {\n    font-weight: 500;\n    background: #f8f9fa;\n    color: inherit;\n    border-radius: 0;\n    margin: 2rem;\n    padding: 1.5rem;\n    padding-bottom: .5rem\n}\n\n.pageinfo-primary {\n    border-style: solid;\n    border-color: {{ site.color }}\n}\n\n.pageinfo-secondary {\n    border-style: solid;\n    border-color: #d95040\n}\n\n.pageinfo-success {\n    border-style: solid;\n    border-color: #3772ff\n}\n\n.pageinfo-info {\n    border-style: solid;\n    border-color: #c0e0de\n}\n\n.pageinfo-warning {\n    border-style: solid;\n    border-color: #ed6a5a\n}\n\n.pageinfo-danger {\n    border-style: solid;\n    border-color: #ed6a5a\n}\n\n.pageinfo-light {\n    border-style: solid;\n    border-color: #d3f3ee\n}\n\n.pageinfo-dark {\n    border-style: solid;\n    border-color: #403f4c\n}\n\nfooter {\n    min-height: 80px\n}\n\n.footer-container {\n    display: flex;\n    align-items: center;\n    width: 100%;\n}\n\n.footer-container > small {\n    width: 100%;\n    text-align: center;\n}\n\n@media(min-width:768px) {\n    .td-offset-anchor:target {\n        display: block;\n        position: relative;\n        top: -4rem;\n        visibility: hidden\n    }\n    h2[id]:before,\n    h3[id]:before,\n    h4[id]:before,\n    h5[id]:before {\n        display: block;\n        margin-top: -5rem;\n        height: 5rem;\n        visibility: hidden\n    }\n\n    .navbar-fixed-top {\n        position: fixed !important;\n        top: 0;\n        right: 0;\n        left: 0;\n        z-index: 1030\n    }\n\n    h1, h2, h3, h4, h5, h6 {\n        scroll-margin-top: 80px !important;\n    }\n}\n\n.heading-anchor {\n    color: transparent;\n    font-size: 1.3rem;\n    margin-left: 6px;\n}\n\n.heading-anchor:hover {\n    color: black;\n}\n\nmain {\n    max-width: 1024px !important;\n}\n\n\n/* images should be horizontally centered and not exceed the width of the container. */\np > img {\n    display: block;\n    max-height: 400px;\n    max-width: 100%;\n    width: auto;\n\n    margin: 2rem auto 2rem;\n}\n\n/* same for video tags */\nvideo {\n    display: block;\n    max-height: 400px;\n    max-width: 100%;\n    width: auto;\n\n    margin: 2rem auto 2rem;\n}\n"
  },
  {
    "path": "docs/assets/css/palette.css",
    "content": "---\nlayout: null\nexcluded_in_search: true\n---\n\n.highlight pre {\n  border: none !important;\n}\n\n.highlight table td { padding: 5px; }\n.highlight table pre { margin: 0; }\n.highlight .cm {\n  color: #999988;\n  font-style: italic;\n}\n.highlight .cp {\n  color: #999999;\n  font-weight: bold;\n}\n.highlight .c1 {\n  color: #999988;\n  font-style: italic;\n}\n.highlight .cs {\n  color: #999999;\n  font-weight: bold;\n  font-style: italic;\n}\n.highlight .c, .highlight .ch, .highlight .cd, .highlight .cpf {\n  color: #999988;\n  font-style: italic;\n}\n.highlight .err {\n  color: #a61717;\n  background-color: #e3d2d2;\n}\n.highlight .gd {\n  color: #000000;\n  background-color: #ffdddd;\n}\n.highlight .ge {\n  color: #000000;\n  font-style: italic;\n}\n.highlight .gr {\n  color: #aa0000;\n}\n.highlight .gh {\n  color: #999999;\n}\n.highlight .gi {\n  color: #000000;\n  background-color: #ddffdd;\n}\n.highlight .go {\n  color: #888888;\n}\n.highlight .gp {\n  color: #555555;\n}\n.highlight .gs {\n  font-weight: bold;\n}\n.highlight .gu {\n  color: #aaaaaa;\n}\n.highlight .gt {\n  color: #aa0000;\n}\n.highlight .kc {\n  color: #000000;\n  font-weight: bold;\n}\n.highlight .kd {\n  color: #000000;\n  font-weight: bold;\n}\n.highlight .kn {\n  color: #000000;\n  font-weight: bold;\n}\n.highlight .kp {\n  color: #000000;\n  font-weight: bold;\n}\n.highlight .kr {\n  color: #000000;\n  font-weight: bold;\n}\n.highlight .kt {\n  color: #445588;\n  font-weight: bold;\n}\n.highlight .k, .highlight .kv {\n  color: #000000;\n  font-weight: bold;\n}\n.highlight .mf {\n  color: #009999;\n}\n.highlight .mh {\n  color: #009999;\n}\n.highlight .il {\n  color: #009999;\n}\n.highlight .mi {\n  color: #009999;\n}\n.highlight .mo {\n  color: #009999;\n}\n.highlight .m, .highlight .mb, .highlight .mx {\n  color: #009999;\n}\n.highlight .sb {\n  color: #d14;\n}\n.highlight .sc {\n  color: #d14;\n}\n.highlight .sd {\n  color: #d14;\n}\n.highlight .s2 {\n  color: #d14;\n}\n.highlight .se {\n  color: #d14;\n}\n.highlight .sh {\n  color: #d14;\n}\n.highlight .si {\n  color: #d14;\n}\n.highlight .sx {\n  color: #d14;\n}\n.highlight .sr {\n  color: #009926;\n}\n.highlight .s1 {\n  color: #d14;\n}\n.highlight .ss {\n  color: #990073;\n}\n.highlight .s, .highlight .sa, .highlight .dl {\n  color: #d14;\n}\n.highlight .na {\n  color: #008080;\n}\n.highlight .bp {\n  color: #999999;\n}\n.highlight .nb {\n  color: #0086B3;\n}\n.highlight .nc {\n  color: #445588;\n  font-weight: bold;\n}\n.highlight .no {\n  color: #008080;\n}\n.highlight .nd {\n  color: #3c5d5d;\n  font-weight: bold;\n}\n.highlight .ni {\n  color: #800080;\n}\n.highlight .ne {\n  color: #990000;\n  font-weight: bold;\n}\n.highlight .nf, .highlight .fm {\n  color: #990000;\n  font-weight: bold;\n}\n.highlight .nl {\n  color: #990000;\n  font-weight: bold;\n}\n.highlight .nn {\n  color: #555555;\n}\n.highlight .nt {\n  color: #000080;\n}\n.highlight .vc {\n  color: #008080;\n}\n.highlight .vg {\n  color: #008080;\n}\n.highlight .vi {\n  color: #008080;\n}\n.highlight .nv, .highlight .vm {\n  color: #008080;\n}\n.highlight .ow {\n  color: #000000;\n  font-weight: bold;\n}\n.highlight .o {\n  color: #000000;\n  font-weight: bold;\n}\n.highlight .w {\n  color: #bbbbbb;\n}\n.highlight {\n  background-color: #f8f8f8;\n}\n"
  },
  {
    "path": "docs/assets/js/main.js",
    "content": "---\nexclude_in_search: true\nlayout: null\n---\n(function($) {\n    'use strict';\n    $(function() {\n        $('[data-toggle=\"tooltip\"]').tooltip();\n        $('[data-toggle=\"popover\"]').popover();\n        $('.popover-dismiss').popover({\n            trigger: 'focus'\n        })\n    });\n\n    function bottomPos(element) {\n        return element.offset().top + element.outerHeight();\n    }\n    $(function() {\n        var promo = $(\".js-td-cover\");\n        if (!promo.length) {\n            return\n        }\n        var promoOffset = bottomPos(promo);\n        var navbarOffset = $('.js-navbar-scroll').offset().top;\n        var threshold = Math.ceil($('.js-navbar-scroll').outerHeight());\n        if ((promoOffset - navbarOffset) < threshold) {\n            $('.js-navbar-scroll').addClass('navbar-bg-onscroll');\n        }\n        $(window).on('scroll', function() {\n            var navtop = $('.js-navbar-scroll').offset().top - $(window).scrollTop();\n            var promoOffset = bottomPos($('.js-td-cover'));\n            var navbarOffset = $('.js-navbar-scroll').offset().top;\n            if ((promoOffset - navbarOffset) < threshold) {\n                $('.js-navbar-scroll').addClass('navbar-bg-onscroll');\n            } else {\n                $('.js-navbar-scroll').removeClass('navbar-bg-onscroll');\n                $('.js-navbar-scroll').addClass('navbar-bg-onscroll--fade');\n            }\n        });\n    });\n}(jQuery));\n(function($) {\n    'use strict';\n    var Search = {\n        init: function() {\n            $(document).ready(function() {\n                $(document).on('keypress', '.td-search-input', function(e) {\n                    if (e.keyCode !== 13) {\n                        return\n                    }\n                    var query = $(this).val();\n                    var searchPage = \"{{ site.url }}{{ site.baseurl }}/search/?q=\" + query;\n                    document.location = searchPage;\n                    return false;\n                });\n            });\n        },\n    };\n    Search.init();\n}(jQuery));\n\nfunction addLinksToAnchors() {\n    const anchors = $('h2[id], h3[id], h4[id], h5[id]');\n\n    if (anchors.length) {\n        anchors.each(function(e) {\n            const element = $(anchors[e]);\n            const targetUrl = window.location.origin + window.location.pathname + '#' + element.attr('id');\n            element.html(element.text() + '<a class=\"heading-anchor\" href=\"' + targetUrl + '\">🔗</a>')\n        });\n    }\n}\n\n$(document).ready(function(){\n    addLinksToAnchors();\n});\n"
  },
  {
    "path": "docs/assets/js/search.js",
    "content": "---\nlayout: null\nexcluded_in_search: true\n---\n(function () {\n\tfunction getQueryVariable(variable) {\n\t\tvar query = window.location.search.substring(1),\n\t\t\tvars = query.split(\"&\");\n\n\t\tfor (var i = 0; i < vars.length; i++) {\n\t\t\tvar pair = vars[i].split(\"=\");\n\n\t\t\tif (pair[0] === variable) {\n\t\t\t\treturn decodeURIComponent(pair[1].replace(/\\+/g, '%20')).trim();\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction getPreview(query, content, previewLength) {\n\t\tpreviewLength = previewLength || (content.length * 2);\n\n\t\tvar parts = query.split(\" \"),\n\t\t\tmatch = content.toLowerCase().indexOf(query.toLowerCase()),\n\t\t\tmatchLength = query.length,\n\t\t\tpreview;\n\n\t\t// Find a relevant location in content\n\t\tfor (var i = 0; i < parts.length; i++) {\n\t\t\tif (match >= 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tmatch = content.toLowerCase().indexOf(parts[i].toLowerCase());\n\t\t\tmatchLength = parts[i].length;\n\t\t}\n\n\t\t// Create preview\n\t\tif (match >= 0) {\n\t\t\tvar start = match - (previewLength / 2),\n\t\t\t\tend = start > 0 ? match + matchLength + (previewLength / 2) : previewLength;\n\n\t\t\tpreview = content.substring(start, end).trim();\n\n\t\t\tif (start > 0) {\n\t\t\t\tpreview = \"...\" + preview;\n\t\t\t}\n\n\t\t\tif (end < content.length) {\n\t\t\t\tpreview = preview + \"...\";\n\t\t\t}\n\n\t\t\t// Highlight query parts\n\t\t\tpreview = preview.replace(new RegExp(\"(\" + parts.join(\"|\") + \")\", \"gi\"), \"<strong>$1</strong>\");\n\t\t} else {\n\t\t\t// Use start of content if no match found\n\t\t\tpreview = content.substring(0, previewLength).trim() + (content.length > previewLength ? \"...\" : \"\");\n\t\t}\n\n\t\treturn preview;\n\t}\n\n\tfunction displaySearchResults(results, query) {\n\t\tvar searchResultsEl = document.getElementById(\"search-results\"),\n\t\t\tsearchProcessEl = document.getElementById(\"search-process\");\n\n\t\tif (results.length) {\n\t\t\tvar resultsHTML = \"\";\n\t\t\tresults.forEach(function (result) {\n\n  \t\t\t\tvar item = window.data[result.ref]\n                                if (item.title) {\n\t\t\t\t\tcontentPreview = getPreview(query, item.content, 170),\n\t\t\t\t\ttitlePreview = getPreview(query, item.title);\n\t\t\t\t\tresultsHTML += \"<li><h4><a href='{{ site.baseurl }}\" + item.url.trim() + \"'>\" + titlePreview + \"</a></h4><p><small>\" + contentPreview + \"</small></p></li>\";\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tsearchResultsEl.innerHTML = resultsHTML;\n\t\t\tsearchProcessEl.innerText = \"Showing\";\n\t\t} else {\n\t\t\tsearchResultsEl.style.display = \"none\";\n\t\t\tsearchProcessEl.innerText = \"No\";\n\t\t}\n\t}\n\n\twindow.index = lunr(function () {\n\t\tthis.field(\"id\");\n\t\tthis.field(\"title\", {boost: 10});\n\t\tthis.field(\"categories\");\n\t\tthis.field(\"url\");\n\t\tthis.field(\"content\");\n\t});\n\n\tvar query = decodeURIComponent((getQueryVariable(\"q\") || \"\").replace(/\\+/g, \"%20\")),\n\t\tsearchQueryContainerEl = document.getElementById(\"search-query-container\"),\n\t\tsearchQueryEl = document.getElementById(\"search-query\");\n\n\tsearchQueryEl.innerText = query;\n        if (query != \"\"){\n   \t\tsearchQueryContainerEl.style.display = \"inline\";\n        }\n\n\tfor (var key in window.data) {\n\t\twindow.index.add(window.data[key]);\n\t}\n\n\tdisplaySearchResults(window.index.search(query), query); // Hand the results off to be displayed\n})();\n"
  },
  {
    "path": "docs/docker-compose.yaml",
    "content": "services:\n  site:\n    container_name: docsy\n    command: jekyll serve --force_polling --drafts\n    image: jekyll/jekyll:4.2.2\n    volumes: \n      - \"./:/srv/jekyll\"\n    ports: \n      - \"4000:4000\"\n"
  },
  {
    "path": "docs/pages/archive.md",
    "content": "---\nlayout: page\ntitle: Articles\npermalink: /archive/\n---\n# News Archive\n\n{% for post in site.posts  %}{% capture this_year %}{{ post.date | date: \"%Y\" }}{% endcapture %}{% capture next_year %}{{ post.previous.date | date: \"%Y\" }}{% endcapture %}\n\n{% if forloop.first %}<h2 class=\"c-archives__year\" id=\"{{ this_year }}-ref\">{{this_year}}</h2>\n<ul class=\"c-archives__list\">{% endif %}\n<li class=\"c-archives__item\">\n  {{ post.date | date: \"%b %-d, %Y\" }}: <a href=\"{{ post.url | prepend: site.baseurl }}\">{{ post.title }}</a>\n  </li>{% if forloop.last %}</ul>{% else %}{% if this_year != next_year %}\n</ul>\n<h2 class=\"c-archives__year\" id=\"{{ next_year }}-ref\">{{next_year}}</h2>\n<ul class=\"c-archives__list\">{% endif %}{% endif %}{% endfor %}\n"
  },
  {
    "path": "docs/pages/feed.xml",
    "content": "---\nlayout: null\npermalink: /feed.xml\n---\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\">\n  <channel>\n    <title>{{ site.title | xml_escape }}</title>\n    <description>{{ site.description | xml_escape }}</description>\n    <link>{{ site.url }}{{ site.baseurl }}/</link>\n    <atom:link href=\"{{ \"/feed.xml\" | prepend: site.baseurl | prepend: site.url }}\" rel=\"self\" type=\"application/rss+xml\"/>\n    <pubDate>{{ site.time | date_to_rfc822 }}</pubDate>\n    <lastBuildDate>{{ site.time | date_to_rfc822 }}</lastBuildDate>\n    <generator>Jekyll v{{ jekyll.version }}</generator>\n    {% for post in site.posts limit:10 %}\n      <item>\n        <title>{{ post.title | xml_escape }}</title>\n        <description>{{ post.content | xml_escape }}</description>\n        <pubDate>{{ post.date | date_to_rfc822 }}</pubDate>\n        <link>{{ post.url | prepend: site.baseurl | prepend: site.url }}</link>\n        <guid isPermaLink=\"true\">{{ post.url | prepend: site.baseurl | prepend: site.url }}</guid>\n        {% for tag in post.tags %}\n        <category>{{ tag | xml_escape }}</category>\n        {% endfor %}\n        {% for cat in post.categories %}\n        <category>{{ cat | xml_escape }}</category>\n        {% endfor %}\n      </item>\n    {% endfor %}\n  </channel>\n</rss>\n"
  },
  {
    "path": "docs/pages/index.md",
    "content": "---\nlayout: page\ntitle: Home\nno_toc: true\npermalink: /\ndescription: G.U.I.D.E is an extension for the Godot Engine that allows you to easily use input from multiple sources.\"\n---\n\n# {{ site.title }}\n\nG.U.I.D.E is an extension for the Godot Engine that allows you to easily use input from multiple sources, such as keyboard, mouse, gamepad and touch in a unified way. Gone are the days, where mouse input was handled differently from joysticks and touch was a totally different beast. No matter where the input comes from - your game code works the same way.\n\n## Getting started\n\nCheck out the [quick start]({{site.baseurl}}/quick-start) page for getting started. If you prefer to learn in video form, there are also some [tutorial videos]({{site.baseurl}}/video-tutorials) available.\n\n## Features\n\n_Note: While the features work pretty well, this plugin hasn't seen a lot of use yet, so be prepared for a few rough edges. Also the documentation still needs expansion. Please report any issues you encounter._\n\n- Unified input detection and handling from multiple sources (keyboard, mouse, gamepad, touch, etc.). All inputs are used in the same way in your game code.\n- Inputs can be modified before being fed into your game code (e.g. for joystick dead-zones, sensitivity, inversion, conversion to 2D/3D coordinates, etc.). \n- Inputs can be assigned to actions and these actions trigger on various conditions (e.g. tap, hold, press, release, combos etc.).\n- Multiple input contexts can be defined, which can be enabled/disabled at runtime. This allows you to easily switch between different input schemes (e.g. in-game, menu, driving, flying, walking, etc.).\n- Overlapping input is automatically prioritized, such that input like _LT+A_ will have precedence over just  _A_.\n- Supports both event-based and polling-based input handling, like Godot's built-in input system.\n- Full support for input rebinding at runtime including collision handling.\n- Built-in support for displaying input prompts in your game. These prompts support complex input combinations (e.g. _LT+A_ or combos like _A > B > A > X > Y_). Prompts can be displayed both as text and as icons. Icons will automatically reflect the actual input device being used (e.g. XBox controller, Playstation controller, keyboard, joystick, etc.).\n- Works nicely alongside Godot's built-in input system, so you can use both in parallel if needed. Can also inject action events into Godot's input system.\n\n\n"
  },
  {
    "path": "docs/pages/search.html",
    "content": "---\ntitle: Search\nsitemap: false\npermalink: /search/\nnot_editable: true\nexcluded_in_search: true\n---\n\n<p><span id=\"search-process\">Loading</span> results <span id=\"search-query-container\" style=\"display: none;\">for \"<strong id=\"search-query\"></strong>\"</span></p>\n\n<ul id=\"search-results\"></ul>\n\n<script>\n\twindow.data = {\n\t\t{% for post in site.docs %}\n\t\t\t\t{% unless post.excluded_in_search %}\n\t\t\t\t\t{% if added %},{% endif %}\n\t\t\t\t\t{% assign added = false %}\n\t\t\t\t\t\"{{ post.url | slugify }}\": {\n\t\t\t\t\t\t\"id\": \"{{ post.url | slugify }}\",\n\t\t\t\t\t\t\"title\": \"{{ post.title | xml_escape }}\",\n\t\t\t\t\t\t\"categories\": \"{{ post.categories | join: \", \" | xml_escape }}\",\n\t\t\t\t\t\t\"url\": \" {{ post.url | xml_escape }}\",\n\t\t\t\t\t\t\"content\": {{ post.content | strip_html | replace_regex: \"[\\s/\\n]+\",\" \" | strip | jsonify }}\n\t\t\t\t\t}\n\t\t\t\t\t{% assign added = true %}\n\t\t\t\t{% endunless %}\n\t\t{% endfor %}\n\t\t{% for post in site.posts %}\n\t\t\t\t{% unless post.excluded_in_search %}\n\t\t\t\t\t{% if added %},{% endif %}\n\t\t\t\t\t{% assign added = false %}\n\t\t\t\t\t\"{{ post.url | slugify }}\": {\n\t\t\t\t\t\t\"id\": \"{{ post.url | slugify }}\",\n\t\t\t\t\t\t\"title\": \"{{ post.title | xml_escape }}\",\n\t\t\t\t\t\t\"categories\": \"{{ post.categories | join: \", \" | xml_escape }}\",\n\t\t\t\t\t\t\"url\": \" {{ post.url | xml_escape }}\",\n\t\t\t\t\t\t\"content\": {{ post.content | strip_html | replace_regex: \"[\\s/\\n]+\",\" \" | strip | jsonify }}\n\t\t\t\t\t}\n\t\t\t\t\t{% assign added = true %}\n\t\t\t\t{% endunless %}\n\t\t{% endfor %}\n\t\t{% for post in site.pages %}\n\t\t\t\t{% unless post.excluded_in_search %}\n\t\t\t\t\t{% if added %},{% endif %}\n\t\t\t\t\t{% assign added = false %}\n\t\t\t\t\t\"{{ post.url | slugify }}\": {\n\t\t\t\t\t\t\"id\": \"{{ post.url | slugify }}\",\n\t\t\t\t\t\t\"title\": \"{{ post.title | xml_escape }}\",\n\t\t\t\t\t\t\"categories\": \"{{ post.categories | join: \", \" | xml_escape }}\",\n\t\t\t\t\t\t\"url\": \" {{ post.url | xml_escape }}\",\n\t\t\t\t\t\t\"content\": {{ post.content | strip_html | replace_regex: \"[\\s/\\n]+\",\" \" | strip | jsonify }}\n\t\t\t\t\t}\n\t\t\t\t\t{% assign added = true %}\n\t\t\t\t{% endunless %}\n\t\t{% endfor %}\n\t};\n</script>\n\n<!-- Lunr.js -->\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/lunr.js/0.7.2/lunr.min.js\" integrity=\"sha512-g75VIuG5LutKZvRY+UxPpzyOSmRpWJBFi20iM+uS/dPf0v+cpU7O3StPRDjSMyFbKz4MkPCTGddHzlzKxEdGzg==\" crossorigin=\"anonymous\" referrerpolicy=\"no-referrer\"></script>\n<script src=\"{{ site.baseurl }}/assets/js/search.js\"></script>\n"
  },
  {
    "path": "docs/pages/sitemap.xml",
    "content": "---\nlayout: null\npermalink: /sitemap.xml\n---\n\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n    <url>\n     <loc>/</loc>\n     <lastmod>{{ \"now\" | date: \"%Y-%m-%d\" }}</lastmod>\n     <changefreq>daily</changefreq>\n    </url>\n{% for section in site.data.toc %}<url>\n     <loc>{{ site.baseurl }}{{ section.url }}/</loc>\n     <lastmod>{{ \"now\" | date: \"%Y-%m-%d\" }}</lastmod>\n     <changefreq>daily</changefreq>\n    </url>\n{% endfor %}\n</urlset>\n"
  },
  {
    "path": "editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\nindent_style = tab\ninsert_final_newline = true\n"
  },
  {
    "path": "export_presets.cfg",
    "content": "[preset.0]\n\nname=\"Android\"\nplatform=\"Android\"\nrunnable=true\ndedicated_server=false\ncustom_features=\"\"\nexport_filter=\"all_resources\"\ninclude_filter=\"\"\nexclude_filter=\"tests,addons/gdUnit4,addons/explore-editor-theme\"\nexport_path=\"C:/Users/kork/Desktop/guide.apk\"\nencryption_include_filters=\"\"\nencryption_exclude_filters=\"\"\nencrypt_pck=false\nencrypt_directory=false\n\n[preset.0.options]\n\ncustom_template/debug=\"\"\ncustom_template/release=\"\"\ngradle_build/use_gradle_build=true\ngradle_build/export_format=0\ngradle_build/min_sdk=\"\"\ngradle_build/target_sdk=\"\"\narchitectures/armeabi-v7a=false\narchitectures/arm64-v8a=true\narchitectures/x86=false\narchitectures/x86_64=false\nversion/code=1\nversion/name=\"\"\npackage/unique_name=\"com.godotneers.guide\"\npackage/name=\"GUIDE\"\npackage/signed=true\npackage/app_category=2\npackage/retain_data_on_uninstall=false\npackage/exclude_from_recents=false\npackage/show_in_android_tv=false\npackage/show_in_app_library=true\npackage/show_as_launcher_app=false\nlauncher_icons/main_192x192=\"\"\nlauncher_icons/adaptive_foreground_432x432=\"\"\nlauncher_icons/adaptive_background_432x432=\"\"\ngraphics/opengl_debug=false\nxr_features/xr_mode=0\nscreen/immersive_mode=true\nscreen/support_small=true\nscreen/support_normal=true\nscreen/support_large=true\nscreen/support_xlarge=true\nuser_data_backup/allow=false\ncommand_line/extra_args=\"\"\napk_expansion/enable=false\napk_expansion/SALT=\"\"\napk_expansion/public_key=\"\"\npermissions/custom_permissions=PackedStringArray()\npermissions/access_checkin_properties=false\npermissions/access_coarse_location=false\npermissions/access_fine_location=false\npermissions/access_location_extra_commands=false\npermissions/access_mock_location=false\npermissions/access_network_state=false\npermissions/access_surface_flinger=false\npermissions/access_wifi_state=false\npermissions/account_manager=false\npermissions/add_voicemail=false\npermissions/authenticate_accounts=false\npermissions/battery_stats=false\npermissions/bind_accessibility_service=false\npermissions/bind_appwidget=false\npermissions/bind_device_admin=false\npermissions/bind_input_method=false\npermissions/bind_nfc_service=false\npermissions/bind_notification_listener_service=false\npermissions/bind_print_service=false\npermissions/bind_remoteviews=false\npermissions/bind_text_service=false\npermissions/bind_vpn_service=false\npermissions/bind_wallpaper=false\npermissions/bluetooth=false\npermissions/bluetooth_admin=false\npermissions/bluetooth_privileged=false\npermissions/brick=false\npermissions/broadcast_package_removed=false\npermissions/broadcast_sms=false\npermissions/broadcast_sticky=false\npermissions/broadcast_wap_push=false\npermissions/call_phone=false\npermissions/call_privileged=false\npermissions/camera=false\npermissions/capture_audio_output=false\npermissions/capture_secure_video_output=false\npermissions/capture_video_output=false\npermissions/change_component_enabled_state=false\npermissions/change_configuration=false\npermissions/change_network_state=false\npermissions/change_wifi_multicast_state=false\npermissions/change_wifi_state=false\npermissions/clear_app_cache=false\npermissions/clear_app_user_data=false\npermissions/control_location_updates=false\npermissions/delete_cache_files=false\npermissions/delete_packages=false\npermissions/device_power=false\npermissions/diagnostic=false\npermissions/disable_keyguard=false\npermissions/dump=false\npermissions/expand_status_bar=false\npermissions/factory_test=false\npermissions/flashlight=false\npermissions/force_back=false\npermissions/get_accounts=false\npermissions/get_package_size=false\npermissions/get_tasks=false\npermissions/get_top_activity_info=false\npermissions/global_search=false\npermissions/hardware_test=false\npermissions/inject_events=false\npermissions/install_location_provider=false\npermissions/install_packages=false\npermissions/install_shortcut=false\npermissions/internal_system_window=false\npermissions/internet=true\npermissions/kill_background_processes=false\npermissions/location_hardware=false\npermissions/manage_accounts=false\npermissions/manage_app_tokens=false\npermissions/manage_documents=false\npermissions/manage_external_storage=false\npermissions/master_clear=false\npermissions/media_content_control=false\npermissions/modify_audio_settings=false\npermissions/modify_phone_state=false\npermissions/mount_format_filesystems=false\npermissions/mount_unmount_filesystems=false\npermissions/nfc=false\npermissions/persistent_activity=false\npermissions/post_notifications=false\npermissions/process_outgoing_calls=false\npermissions/read_calendar=false\npermissions/read_call_log=false\npermissions/read_contacts=false\npermissions/read_external_storage=false\npermissions/read_frame_buffer=false\npermissions/read_history_bookmarks=false\npermissions/read_input_state=false\npermissions/read_logs=false\npermissions/read_phone_state=false\npermissions/read_profile=false\npermissions/read_sms=false\npermissions/read_social_stream=false\npermissions/read_sync_settings=false\npermissions/read_sync_stats=false\npermissions/read_user_dictionary=false\npermissions/reboot=false\npermissions/receive_boot_completed=false\npermissions/receive_mms=false\npermissions/receive_sms=false\npermissions/receive_wap_push=false\npermissions/record_audio=false\npermissions/reorder_tasks=false\npermissions/restart_packages=false\npermissions/send_respond_via_message=false\npermissions/send_sms=false\npermissions/set_activity_watcher=false\npermissions/set_alarm=false\npermissions/set_always_finish=false\npermissions/set_animation_scale=false\npermissions/set_debug_app=false\npermissions/set_orientation=false\npermissions/set_pointer_speed=false\npermissions/set_preferred_applications=false\npermissions/set_process_limit=false\npermissions/set_time=false\npermissions/set_time_zone=false\npermissions/set_wallpaper=false\npermissions/set_wallpaper_hints=false\npermissions/signal_persistent_processes=false\npermissions/status_bar=false\npermissions/subscribed_feeds_read=false\npermissions/subscribed_feeds_write=false\npermissions/system_alert_window=false\npermissions/transmit_ir=false\npermissions/uninstall_shortcut=false\npermissions/update_device_stats=false\npermissions/use_credentials=false\npermissions/use_sip=false\npermissions/vibrate=false\npermissions/wake_lock=false\npermissions/write_apn_settings=false\npermissions/write_calendar=false\npermissions/write_call_log=false\npermissions/write_contacts=false\npermissions/write_external_storage=false\npermissions/write_gservices=false\npermissions/write_history_bookmarks=false\npermissions/write_profile=false\npermissions/write_secure_settings=false\npermissions/write_settings=false\npermissions/write_sms=false\npermissions/write_social_stream=false\npermissions/write_sync_settings=false\npermissions/write_user_dictionary=false\ndotnet/include_scripts_content=false\ndotnet/include_debug_symbols=true\ndotnet/embed_build_outputs=false\n"
  },
  {
    "path": "ggg.toml",
    "content": "\n[project]\ngodot = \"4.2-stable\"\n\n[[dependency]]\nname = \"gdunit4\"\ngit = \"https://github.com/godot-gdunit-labs/gdUnit4\"\nrev = \"v4.5.0\"\n"
  },
  {
    "path": "guide_examples/2d_axis_mapping/2d_axis_mapping.gd",
    "content": "extends Node\n\n@export var mapping_context:GUIDEMappingContext\n\nfunc _ready() -> void:\n\tGUIDE.enable_mapping_context(mapping_context)\n"
  },
  {
    "path": "guide_examples/2d_axis_mapping/2d_axis_mapping.gd.uid",
    "content": "uid://cl57fk6xess4f\n"
  },
  {
    "path": "guide_examples/2d_axis_mapping/2d_axis_mapping.tscn",
    "content": "[gd_scene load_steps=9 format=3 uid=\"uid://dvbxt8jyo8okq\"]\n\n[ext_resource type=\"Texture2D\" uid=\"uid://byjxtsekdl8t2\" path=\"res://guide_examples/shared/godot_logo.svg\" id=\"1_4tef3\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/2d_axis_mapping/2d_axis_mapping.gd\" id=\"1_vdstu\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/2d_axis_mapping/player.gd\" id=\"2_54pxx\"]\n[ext_resource type=\"Resource\" uid=\"uid://2hl7iqpondhi\" path=\"res://guide_examples/input_scheme_switching/mapping_contexts/keyboard_scheme.tres\" id=\"2_jtcd0\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://dkr80d2pi0d41\" path=\"res://addons/guide/debugger/guide_debugger.tscn\" id=\"5_ewox0\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/shared/instructions_label.gd\" id=\"5_h0lne\"]\n[ext_resource type=\"Resource\" uid=\"uid://cxn2ibe1mn3sb\" path=\"res://guide_examples/input_scheme_switching/mapping_contexts/actions/move.tres\" id=\"8_581qd\"]\n[ext_resource type=\"Theme\" uid=\"uid://dot0gi1yoqmrl\" path=\"res://guide_examples/shared/ui_theme.tres\" id=\"8_p4vbo\"]\n\n[node name=\"2DAxisMapping\" type=\"Node\"]\nscript = ExtResource(\"1_vdstu\")\nmapping_context = ExtResource(\"2_jtcd0\")\n\n[node name=\"Player\" type=\"Sprite2D\" parent=\".\"]\nposition = Vector2(546, 317)\ntexture = ExtResource(\"1_4tef3\")\nscript = ExtResource(\"2_54pxx\")\nmove_action = ExtResource(\"8_581qd\")\n\n[node name=\"UI Layer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"Instructions Label\" type=\"RichTextLabel\" parent=\"UI Layer\"]\nanchors_preset = 1\nanchor_left = 1.0\nanchor_right = 1.0\noffset_left = -286.0\noffset_top = 24.0\noffset_right = -39.0\noffset_bottom = 47.0\ngrow_horizontal = 0\ntheme = ExtResource(\"8_p4vbo\")\nscript = ExtResource(\"5_h0lne\")\ninstructions_text = \"Use %s to move the player.\"\nactions = Array[Resource(\"res://addons/guide/guide_action.gd\")]([ExtResource(\"8_581qd\")])\n\n[node name=\"Debug Layer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"GuideDebugger\" parent=\"Debug Layer\" instance=ExtResource(\"5_ewox0\")]\ntheme = ExtResource(\"8_p4vbo\")\nmetadata/_edit_lock_ = true\n"
  },
  {
    "path": "guide_examples/2d_axis_mapping/mapping_contexts/2d_axis_mapping.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMappingContext\" load_steps=21 format=3 uid=\"uid://2hl7iqpondhj\"]\n\n[ext_resource type=\"Resource\" uid=\"uid://cxn2ibe1mn3sb\" path=\"res://guide_examples/input_scheme_switching/mapping_contexts/actions/move.tres\" id=\"1_5vw7l\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_key.gd\" id=\"2_0yrlp\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_input_mapping.gd\" id=\"3_ad6sj\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_negate.gd\" id=\"3_nlxx1\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action_mapping.gd\" id=\"4_16vkk\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_input_swizzle.gd\" id=\"5_qr6a5\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_mapping_context.gd\" id=\"8_r1avn\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_t51n6\"]\nscript = ExtResource(\"2_0yrlp\")\nkey = 87\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_ct0te\"]\nscript = ExtResource(\"3_nlxx1\")\nx = true\ny = true\nz = true\n\n[sub_resource type=\"Resource\" id=\"Resource_qm6gq\"]\nscript = ExtResource(\"5_qr6a5\")\norder = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_yewp7\"]\nscript = ExtResource(\"3_ad6sj\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_t51n6\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_ct0te\"), SubResource(\"Resource_qm6gq\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_c40re\"]\nscript = ExtResource(\"2_0yrlp\")\nkey = 65\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_rkxfa\"]\nscript = ExtResource(\"3_nlxx1\")\nx = true\ny = true\nz = true\n\n[sub_resource type=\"Resource\" id=\"Resource_6qgwf\"]\nscript = ExtResource(\"3_ad6sj\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_c40re\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_rkxfa\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_2t3ts\"]\nscript = ExtResource(\"2_0yrlp\")\nkey = 83\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_eoiu5\"]\nscript = ExtResource(\"5_qr6a5\")\norder = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_bilhe\"]\nscript = ExtResource(\"3_ad6sj\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_2t3ts\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_eoiu5\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_lften\"]\nscript = ExtResource(\"2_0yrlp\")\nkey = 68\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_v2qov\"]\nscript = ExtResource(\"3_ad6sj\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_lften\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_u0xy6\"]\nscript = ExtResource(\"4_16vkk\")\naction = ExtResource(\"1_5vw7l\")\ninput_mappings = Array[ExtResource(\"3_ad6sj\")]([SubResource(\"Resource_yewp7\"), SubResource(\"Resource_6qgwf\"), SubResource(\"Resource_bilhe\"), SubResource(\"Resource_v2qov\")])\n\n[resource]\nscript = ExtResource(\"8_r1avn\")\ndisplay_name = \"\"\nmappings = Array[ExtResource(\"4_16vkk\")]([SubResource(\"Resource_u0xy6\")])\n"
  },
  {
    "path": "guide_examples/2d_axis_mapping/mapping_contexts/move.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://cxn2ibe1mn3sa\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_anxy1\"]\n\n[resource]\nscript = ExtResource(\"1_anxy1\")\nname = &\"\"\naction_value_type = 2\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/2d_axis_mapping/player.gd",
    "content": "## A very simple player script for a player who can only move.\nextends Node2D\n\n@export var speed:float = 300\n@export var move_action:GUIDEAction\n\nfunc _process(delta:float) -> void:\n\t# GUIDE already gives us a full 2D axis. We don't need to build it\n\t# ourselves using Input.get_vector.\n\tposition += move_action.value_axis_2d.normalized() * speed * delta\n"
  },
  {
    "path": "guide_examples/2d_axis_mapping/player.gd.uid",
    "content": "uid://b3l3w1askqxgo\n"
  },
  {
    "path": "guide_examples/action_priority/action_priority.gd",
    "content": "extends Node2D\n\n\n@export var mapping_context:GUIDEMappingContext\n@export var spell_toggle:GUIDEAction\n\n@onready var _layer_1:Control = %Layer1\n@onready var _layer_2:Control = %Layer2\n\n\nfunc _ready() -> void:\n\tGUIDE.enable_mapping_context(mapping_context)\n\tspell_toggle.triggered.connect(func() -> void: _layer_1.hide(); _layer_2.show())\n\tspell_toggle.completed.connect(func() -> void: _layer_1.show(); _layer_2.hide())\n"
  },
  {
    "path": "guide_examples/action_priority/action_priority.gd.uid",
    "content": "uid://cycxhrywjuggp\n"
  },
  {
    "path": "guide_examples/action_priority/action_priority.tscn",
    "content": "[gd_scene load_steps=25 format=3 uid=\"uid://c03o20jchp7kb\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/action_priority/action_priority.gd\" id=\"1_segid\"]\n[ext_resource type=\"Resource\" uid=\"uid://ragqbe7yjfwe\" path=\"res://guide_examples/action_priority/mapping_contexts/action_priority.tres\" id=\"2_spx2e\"]\n[ext_resource type=\"Resource\" uid=\"uid://c5eq1avod0lu8\" path=\"res://guide_examples/action_priority/mapping_contexts/actions/spell_toggle.tres\" id=\"3_k38f6\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://dkr80d2pi0d41\" path=\"res://addons/guide/debugger/guide_debugger.tscn\" id=\"3_ocaq1\"]\n[ext_resource type=\"Theme\" uid=\"uid://dot0gi1yoqmrl\" path=\"res://guide_examples/shared/ui_theme.tres\" id=\"4_ur8xb\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/shared/instructions_label.gd\" id=\"4_v7mqo\"]\n[ext_resource type=\"Resource\" uid=\"uid://esf4ilpf0inv\" path=\"res://guide_examples/action_priority/mapping_contexts/actions/acid_bolt.tres\" id=\"5_oqj0p\"]\n[ext_resource type=\"Resource\" uid=\"uid://bhq3gby2yiibf\" path=\"res://guide_examples/action_priority/mapping_contexts/actions/acid_enchantment.tres\" id=\"6_ue1ny\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://bsv0uwfyqbbbw\" path=\"res://guide_examples/action_priority/dpad_spells/dpad_spells.tscn\" id=\"7_48cit\"]\n[ext_resource type=\"Resource\" uid=\"uid://cdhpb7yuq5pkb\" path=\"res://guide_examples/action_priority/mapping_contexts/actions/mana_bolt.tres\" id=\"7_ruu3d\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://emjksgvvx8kr\" path=\"res://guide_examples/action_priority/icons/fireball-acid-3.png\" id=\"8_pbht4\"]\n[ext_resource type=\"Resource\" uid=\"uid://dsp8h1ycwd6tt\" path=\"res://guide_examples/action_priority/mapping_contexts/actions/heal.tres\" id=\"8_tl0ch\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://do0b76wher5pk\" path=\"res://guide_examples/action_priority/icons/fireball-sky-3.png\" id=\"9_6ehip\"]\n[ext_resource type=\"Resource\" uid=\"uid://b5plj56pss47x\" path=\"res://guide_examples/action_priority/mapping_contexts/actions/shield.tres\" id=\"9_rbwtd\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bb2whckygsgvj\" path=\"res://guide_examples/action_priority/icons/heal-royal-3.png\" id=\"10_vp0as\"]\n[ext_resource type=\"Resource\" uid=\"uid://do3hivxhwoqvi\" path=\"res://guide_examples/action_priority/mapping_contexts/actions/mana_enchantment.tres\" id=\"11_223o5\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://dvy7bcy022rqq\" path=\"res://guide_examples/action_priority/icons/protect-blue-2.png\" id=\"11_t3r7p\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://dbwgq8udtj2hp\" path=\"res://guide_examples/action_priority/icons/enchant-acid-3.png\" id=\"12_2ht6b\"]\n[ext_resource type=\"Resource\" uid=\"uid://dtr3jy86gc3rk\" path=\"res://guide_examples/action_priority/mapping_contexts/actions/healing_enchantment.tres\" id=\"12_4o7kv\"]\n[ext_resource type=\"Resource\" uid=\"uid://bfskfiw1k8574\" path=\"res://guide_examples/action_priority/mapping_contexts/actions/fire_enchantment.tres\" id=\"13_6g6j5\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://b3j6yx4455rj0\" path=\"res://guide_examples/action_priority/icons/enchant-blue-3.png\" id=\"13_yufl8\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://djexj402ii0qp\" path=\"res://guide_examples/action_priority/icons/enchant-jade-3.png\" id=\"14_dpiqo\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://cx8f0hljh5dhs\" path=\"res://guide_examples/action_priority/spell_indicator/spell_indicator.tscn\" id=\"14_gmycm\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bcls6cfcyhf0t\" path=\"res://guide_examples/action_priority/icons/enchant-red-3.png\" id=\"15_xjepw\"]\n\n[node name=\"ActionPriority\" type=\"Node2D\"]\nscript = ExtResource(\"1_segid\")\nmapping_context = ExtResource(\"2_spx2e\")\nspell_toggle = ExtResource(\"3_k38f6\")\n\n[node name=\"UILayer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"Instructions Label\" type=\"RichTextLabel\" parent=\"UILayer\"]\nanchors_preset = 1\nanchor_left = 1.0\nanchor_right = 1.0\noffset_left = -78.0\noffset_top = 19.0\noffset_right = -38.0\noffset_bottom = 42.0\ngrow_horizontal = 0\ntheme = ExtResource(\"4_ur8xb\")\nscript = ExtResource(\"4_v7mqo\")\ninstructions_text = \"%s - acid bolt\n%s - mana bolt\n%s - heal\n%s - shield\n%s - acid enchantment\n%s - mana enchantment\n%s - healing enchantment\n%s - fire enchantment\"\nactions = Array[Resource(\"res://addons/guide/guide_action.gd\")]([ExtResource(\"5_oqj0p\"), ExtResource(\"7_ruu3d\"), ExtResource(\"8_tl0ch\"), ExtResource(\"9_rbwtd\"), ExtResource(\"6_ue1ny\"), ExtResource(\"11_223o5\"), ExtResource(\"12_4o7kv\"), ExtResource(\"13_6g6j5\")])\n\n[node name=\"SpellIndicators\" type=\"Node2D\" parent=\"UILayer\"]\nposition = Vector2(1149, 781)\n\n[node name=\"AcidBolt\" parent=\"UILayer/SpellIndicators\" instance=ExtResource(\"14_gmycm\")]\naction = ExtResource(\"5_oqj0p\")\ntexture = ExtResource(\"8_pbht4\")\n\n[node name=\"ManaBolt\" parent=\"UILayer/SpellIndicators\" instance=ExtResource(\"14_gmycm\")]\naction = ExtResource(\"7_ruu3d\")\ntexture = ExtResource(\"9_6ehip\")\n\n[node name=\"Heal\" parent=\"UILayer/SpellIndicators\" instance=ExtResource(\"14_gmycm\")]\naction = ExtResource(\"8_tl0ch\")\ntexture = ExtResource(\"10_vp0as\")\n\n[node name=\"Shield\" parent=\"UILayer/SpellIndicators\" instance=ExtResource(\"14_gmycm\")]\naction = ExtResource(\"9_rbwtd\")\ntexture = ExtResource(\"11_t3r7p\")\n\n[node name=\"AcidEnchant\" parent=\"UILayer/SpellIndicators\" instance=ExtResource(\"14_gmycm\")]\naction = ExtResource(\"6_ue1ny\")\ntexture = ExtResource(\"12_2ht6b\")\n\n[node name=\"ManaEnchant\" parent=\"UILayer/SpellIndicators\" instance=ExtResource(\"14_gmycm\")]\naction = ExtResource(\"11_223o5\")\ntexture = ExtResource(\"13_yufl8\")\n\n[node name=\"HealingEnchant\" parent=\"UILayer/SpellIndicators\" instance=ExtResource(\"14_gmycm\")]\naction = ExtResource(\"12_4o7kv\")\ntexture = ExtResource(\"14_dpiqo\")\n\n[node name=\"FireEnchant\" parent=\"UILayer/SpellIndicators\" instance=ExtResource(\"14_gmycm\")]\naction = ExtResource(\"13_6g6j5\")\ntexture = ExtResource(\"15_xjepw\")\n\n[node name=\"Spells\" type=\"MarginContainer\" parent=\"UILayer\"]\nanchors_preset = 8\nanchor_left = 0.5\nanchor_top = 0.5\nanchor_right = 0.5\nanchor_bottom = 0.5\noffset_left = -529.0\noffset_top = 5.0\noffset_right = -85.0\noffset_bottom = 455.0\ngrow_horizontal = 2\ngrow_vertical = 2\npivot_offset = Vector2(171, 194)\n\n[node name=\"Layer1\" parent=\"UILayer/Spells\" instance=ExtResource(\"7_48cit\")]\nunique_name_in_owner = true\nlayout_mode = 2\nup = ExtResource(\"8_pbht4\")\nleft = ExtResource(\"9_6ehip\")\nright = ExtResource(\"10_vp0as\")\ndown = ExtResource(\"11_t3r7p\")\n\n[node name=\"Layer2\" parent=\"UILayer/Spells\" instance=ExtResource(\"7_48cit\")]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\nup = ExtResource(\"12_2ht6b\")\nleft = ExtResource(\"13_yufl8\")\nright = ExtResource(\"14_dpiqo\")\ndown = ExtResource(\"15_xjepw\")\n\n[node name=\"DebugLayer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"GuideDebugger\" parent=\"DebugLayer\" instance=ExtResource(\"3_ocaq1\")]\ntheme = ExtResource(\"4_ur8xb\")\n"
  },
  {
    "path": "guide_examples/action_priority/dpad_spells/dpad_spells.gd",
    "content": "@tool\nextends GridContainer\n\n@onready var _up:TextureRect = %Up\n@onready var _left:TextureRect = %Left\n@onready var _right:TextureRect = %Right\n@onready var _down:TextureRect = %Down\n\n\n@export var up:Texture2D:\n\tset(value):\n\t\tif value == up:\n\t\t\treturn\n\t\tup = value\n\t\t_refresh()\n\t\t\n\t\t\n@export var left:Texture2D:\n\tset(value):\n\t\tif value == left:\n\t\t\treturn\n\t\tleft = value\n\t\t_refresh()\n\t\t\n\t\t\n@export var right:Texture2D:\n\tset(value):\n\t\tif value == right:\n\t\t\treturn\n\t\tright= value\n\t\t_refresh()\n\t\t\n@export var down:Texture2D:\n\tset(value):\n\t\tif value == down:\n\t\t\treturn\n\t\tdown = value\n\t\t_refresh()\n\n\nfunc _ready() -> void:\n\t_refresh()\n\t\n\t\nfunc _refresh() -> void:\n\tif not is_node_ready():\n\t\treturn\n\t\t\n\t_up.texture = up\n\t_down.texture = down\n\t_left.texture = left\n\t_right.texture = right\n"
  },
  {
    "path": "guide_examples/action_priority/dpad_spells/dpad_spells.gd.uid",
    "content": "uid://wsbm3iwylkto\n"
  },
  {
    "path": "guide_examples/action_priority/dpad_spells/dpad_spells.tscn",
    "content": "[gd_scene load_steps=2 format=3 uid=\"uid://bsv0uwfyqbbbw\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/action_priority/dpad_spells/dpad_spells.gd\" id=\"1_pl5jh\"]\n\n[node name=\"DpadSpells\" type=\"GridContainer\"]\noffset_right = 323.0\noffset_bottom = 329.0\ncolumns = 3\nscript = ExtResource(\"1_pl5jh\")\n\n[node name=\"Spacer\" type=\"Control\" parent=\".\"]\nlayout_mode = 2\n\n[node name=\"Up\" type=\"TextureRect\" parent=\".\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\nexpand_mode = 1\nstretch_mode = 5\n\n[node name=\"Spacer2\" type=\"Control\" parent=\".\"]\nlayout_mode = 2\n\n[node name=\"Left\" type=\"TextureRect\" parent=\".\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\nexpand_mode = 1\nstretch_mode = 5\n\n[node name=\"Spacer3\" type=\"Control\" parent=\".\"]\nlayout_mode = 2\n\n[node name=\"Right\" type=\"TextureRect\" parent=\".\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\nexpand_mode = 1\nstretch_mode = 5\n\n[node name=\"Spacer4\" type=\"Control\" parent=\".\"]\nlayout_mode = 2\n\n[node name=\"Down\" type=\"TextureRect\" parent=\".\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 3\nsize_flags_vertical = 3\nexpand_mode = 1\nstretch_mode = 5\n\n[node name=\"Spacer5\" type=\"Control\" parent=\".\"]\nlayout_mode = 2\n"
  },
  {
    "path": "guide_examples/action_priority/icons/enchant-acid-3.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://dbwgq8udtj2hp\"\npath=\"res://.godot/imported/enchant-acid-3.png-e1947aa390c674d1399abe634bc54deb.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://guide_examples/action_priority/icons/enchant-acid-3.png\"\ndest_files=[\"res://.godot/imported/enchant-acid-3.png-e1947aa390c674d1399abe634bc54deb.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "guide_examples/action_priority/icons/enchant-blue-3.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://b3j6yx4455rj0\"\npath=\"res://.godot/imported/enchant-blue-3.png-7fbde34e03a870571687bd67f54ff1a7.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://guide_examples/action_priority/icons/enchant-blue-3.png\"\ndest_files=[\"res://.godot/imported/enchant-blue-3.png-7fbde34e03a870571687bd67f54ff1a7.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "guide_examples/action_priority/icons/enchant-jade-3.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://djexj402ii0qp\"\npath=\"res://.godot/imported/enchant-jade-3.png-dbada551fd0e4cb0fb15a3867cb2ef6b.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://guide_examples/action_priority/icons/enchant-jade-3.png\"\ndest_files=[\"res://.godot/imported/enchant-jade-3.png-dbada551fd0e4cb0fb15a3867cb2ef6b.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "guide_examples/action_priority/icons/enchant-red-3.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bcls6cfcyhf0t\"\npath=\"res://.godot/imported/enchant-red-3.png-82aa47d9224cc61c8bdf354866612b90.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://guide_examples/action_priority/icons/enchant-red-3.png\"\ndest_files=[\"res://.godot/imported/enchant-red-3.png-82aa47d9224cc61c8bdf354866612b90.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "guide_examples/action_priority/icons/fireball-acid-3.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://emjksgvvx8kr\"\npath=\"res://.godot/imported/fireball-acid-3.png-17a549b7e763ef3fa4170fd9d37c6d27.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://guide_examples/action_priority/icons/fireball-acid-3.png\"\ndest_files=[\"res://.godot/imported/fireball-acid-3.png-17a549b7e763ef3fa4170fd9d37c6d27.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "guide_examples/action_priority/icons/fireball-sky-3.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://do0b76wher5pk\"\npath=\"res://.godot/imported/fireball-sky-3.png-ef89177b310ec94cab7573ba28dfb564.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://guide_examples/action_priority/icons/fireball-sky-3.png\"\ndest_files=[\"res://.godot/imported/fireball-sky-3.png-ef89177b310ec94cab7573ba28dfb564.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "guide_examples/action_priority/icons/heal-royal-3.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://bb2whckygsgvj\"\npath=\"res://.godot/imported/heal-royal-3.png-a1b67309c29c3e0722cb4efa430d5f09.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://guide_examples/action_priority/icons/heal-royal-3.png\"\ndest_files=[\"res://.godot/imported/heal-royal-3.png-a1b67309c29c3e0722cb4efa430d5f09.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "guide_examples/action_priority/icons/protect-blue-2.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://dvy7bcy022rqq\"\npath=\"res://.godot/imported/protect-blue-2.png-abb9ef1d1e06e180429074d0e9b24edb.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://guide_examples/action_priority/icons/protect-blue-2.png\"\ndest_files=[\"res://.godot/imported/protect-blue-2.png-abb9ef1d1e06e180429074d0e9b24edb.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "guide_examples/action_priority/mapping_contexts/action_priority.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMappingContext\" load_steps=56 format=3 uid=\"uid://ragqbe7yjfwe\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_mapping_context.gd\" id=\"1_asqiw\"]\n[ext_resource type=\"Resource\" uid=\"uid://bhq3gby2yiibf\" path=\"res://guide_examples/action_priority/mapping_contexts/actions/acid_enchantment.tres\" id=\"1_pwefn\"]\n[ext_resource type=\"Resource\" uid=\"uid://c5eq1avod0lu8\" path=\"res://guide_examples/action_priority/mapping_contexts/actions/spell_toggle.tres\" id=\"2_swo1r\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_input_mapping.gd\" id=\"4_6rx1x\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_pressed.gd\" id=\"4_n7mmu\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_joy_button.gd\" id=\"5_cxbyx\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_chorded_action.gd\" id=\"5_w86oe\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action_mapping.gd\" id=\"6_4koxr\"]\n[ext_resource type=\"Resource\" uid=\"uid://esf4ilpf0inv\" path=\"res://guide_examples/action_priority/mapping_contexts/actions/acid_bolt.tres\" id=\"7_35imv\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_joy_axis_1d.gd\" id=\"8_avuuj\"]\n[ext_resource type=\"Resource\" uid=\"uid://do3hivxhwoqvi\" path=\"res://guide_examples/action_priority/mapping_contexts/actions/mana_enchantment.tres\" id=\"8_lmhmq\"]\n[ext_resource type=\"Resource\" uid=\"uid://dtr3jy86gc3rk\" path=\"res://guide_examples/action_priority/mapping_contexts/actions/healing_enchantment.tres\" id=\"9_xfl3h\"]\n[ext_resource type=\"Resource\" uid=\"uid://cdhpb7yuq5pkb\" path=\"res://guide_examples/action_priority/mapping_contexts/actions/mana_bolt.tres\" id=\"10_krd45\"]\n[ext_resource type=\"Resource\" uid=\"uid://bfskfiw1k8574\" path=\"res://guide_examples/action_priority/mapping_contexts/actions/fire_enchantment.tres\" id=\"10_m3nrn\"]\n[ext_resource type=\"Resource\" uid=\"uid://dsp8h1ycwd6tt\" path=\"res://guide_examples/action_priority/mapping_contexts/actions/heal.tres\" id=\"12_w4t7r\"]\n[ext_resource type=\"Resource\" uid=\"uid://b5plj56pss47x\" path=\"res://guide_examples/action_priority/mapping_contexts/actions/shield.tres\" id=\"14_qai3i\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_2fxes\"]\nscript = ExtResource(\"5_cxbyx\")\nbutton = 11\njoy_index = -1\n\n[sub_resource type=\"Resource\" id=\"Resource_jhb3c\"]\nscript = ExtResource(\"5_w86oe\")\naction = ExtResource(\"2_swo1r\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_i54ow\"]\nscript = ExtResource(\"4_n7mmu\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_fjuhd\"]\nscript = ExtResource(\"4_6rx1x\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_2fxes\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_jhb3c\"), SubResource(\"Resource_i54ow\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_ai5ps\"]\nscript = ExtResource(\"6_4koxr\")\naction = ExtResource(\"1_pwefn\")\ninput_mappings = Array[ExtResource(\"4_6rx1x\")]([SubResource(\"Resource_fjuhd\")])\nmetadata/_guide_input_mappings_collapsed = true\n\n[sub_resource type=\"Resource\" id=\"Resource_w2qty\"]\nscript = ExtResource(\"5_cxbyx\")\nbutton = 13\njoy_index = -1\n\n[sub_resource type=\"Resource\" id=\"Resource_rl1q7\"]\nscript = ExtResource(\"5_w86oe\")\naction = ExtResource(\"2_swo1r\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_4y1rh\"]\nscript = ExtResource(\"4_n7mmu\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_d8nq3\"]\nscript = ExtResource(\"4_6rx1x\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_w2qty\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_rl1q7\"), SubResource(\"Resource_4y1rh\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_jwd6q\"]\nscript = ExtResource(\"6_4koxr\")\naction = ExtResource(\"8_lmhmq\")\ninput_mappings = Array[ExtResource(\"4_6rx1x\")]([SubResource(\"Resource_d8nq3\")])\nmetadata/_guide_input_mappings_collapsed = true\n\n[sub_resource type=\"Resource\" id=\"Resource_mxqg0\"]\nscript = ExtResource(\"5_cxbyx\")\nbutton = 14\njoy_index = -1\n\n[sub_resource type=\"Resource\" id=\"Resource_ip7ia\"]\nscript = ExtResource(\"5_w86oe\")\naction = ExtResource(\"2_swo1r\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_i6wfw\"]\nscript = ExtResource(\"4_n7mmu\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_m8ya7\"]\nscript = ExtResource(\"4_6rx1x\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_mxqg0\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_ip7ia\"), SubResource(\"Resource_i6wfw\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_22c0i\"]\nscript = ExtResource(\"6_4koxr\")\naction = ExtResource(\"9_xfl3h\")\ninput_mappings = Array[ExtResource(\"4_6rx1x\")]([SubResource(\"Resource_m8ya7\")])\nmetadata/_guide_input_mappings_collapsed = true\n\n[sub_resource type=\"Resource\" id=\"Resource_u2m40\"]\nscript = ExtResource(\"5_cxbyx\")\nbutton = 12\njoy_index = -1\n\n[sub_resource type=\"Resource\" id=\"Resource_7owy8\"]\nscript = ExtResource(\"5_w86oe\")\naction = ExtResource(\"2_swo1r\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_tchgu\"]\nscript = ExtResource(\"4_n7mmu\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_2q1gn\"]\nscript = ExtResource(\"4_6rx1x\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_u2m40\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_7owy8\"), SubResource(\"Resource_tchgu\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_4acdf\"]\nscript = ExtResource(\"6_4koxr\")\naction = ExtResource(\"10_m3nrn\")\ninput_mappings = Array[ExtResource(\"4_6rx1x\")]([SubResource(\"Resource_2q1gn\")])\nmetadata/_guide_input_mappings_collapsed = true\n\n[sub_resource type=\"Resource\" id=\"Resource_5sjq4\"]\nscript = ExtResource(\"5_cxbyx\")\nbutton = 11\njoy_index = -1\n\n[sub_resource type=\"Resource\" id=\"Resource_fkk8p\"]\nscript = ExtResource(\"4_n7mmu\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_vswh4\"]\nscript = ExtResource(\"4_6rx1x\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_5sjq4\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_fkk8p\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_8wvmf\"]\nscript = ExtResource(\"6_4koxr\")\naction = ExtResource(\"7_35imv\")\ninput_mappings = Array[ExtResource(\"4_6rx1x\")]([SubResource(\"Resource_vswh4\")])\nmetadata/_guide_input_mappings_collapsed = true\n\n[sub_resource type=\"Resource\" id=\"Resource_mbfh8\"]\nscript = ExtResource(\"5_cxbyx\")\nbutton = 13\njoy_index = -1\n\n[sub_resource type=\"Resource\" id=\"Resource_umt5k\"]\nscript = ExtResource(\"4_n7mmu\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_b8our\"]\nscript = ExtResource(\"4_6rx1x\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_mbfh8\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_umt5k\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_8p32p\"]\nscript = ExtResource(\"6_4koxr\")\naction = ExtResource(\"10_krd45\")\ninput_mappings = Array[ExtResource(\"4_6rx1x\")]([SubResource(\"Resource_b8our\")])\nmetadata/_guide_input_mappings_collapsed = true\n\n[sub_resource type=\"Resource\" id=\"Resource_utha0\"]\nscript = ExtResource(\"5_cxbyx\")\nbutton = 14\njoy_index = -1\n\n[sub_resource type=\"Resource\" id=\"Resource_sm46b\"]\nscript = ExtResource(\"4_n7mmu\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_1f76r\"]\nscript = ExtResource(\"4_6rx1x\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_utha0\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_sm46b\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_73ywc\"]\nscript = ExtResource(\"6_4koxr\")\naction = ExtResource(\"12_w4t7r\")\ninput_mappings = Array[ExtResource(\"4_6rx1x\")]([SubResource(\"Resource_1f76r\")])\nmetadata/_guide_input_mappings_collapsed = true\n\n[sub_resource type=\"Resource\" id=\"Resource_ov80l\"]\nscript = ExtResource(\"5_cxbyx\")\nbutton = 12\njoy_index = -1\n\n[sub_resource type=\"Resource\" id=\"Resource_wu6o8\"]\nscript = ExtResource(\"4_n7mmu\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_ggg1r\"]\nscript = ExtResource(\"4_6rx1x\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_ov80l\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_wu6o8\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_rleu1\"]\nscript = ExtResource(\"6_4koxr\")\naction = ExtResource(\"14_qai3i\")\ninput_mappings = Array[ExtResource(\"4_6rx1x\")]([SubResource(\"Resource_ggg1r\")])\nmetadata/_guide_input_mappings_collapsed = true\n\n[sub_resource type=\"Resource\" id=\"Resource_m51uq\"]\nscript = ExtResource(\"8_avuuj\")\naxis = 4\njoy_index = -1\n\n[sub_resource type=\"Resource\" id=\"Resource_2hg0t\"]\nscript = ExtResource(\"4_6rx1x\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_m51uq\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_uj8l6\"]\nscript = ExtResource(\"6_4koxr\")\naction = ExtResource(\"2_swo1r\")\ninput_mappings = Array[ExtResource(\"4_6rx1x\")]([SubResource(\"Resource_2hg0t\")])\n\n[resource]\nscript = ExtResource(\"1_asqiw\")\ndisplay_name = \"\"\nmappings = Array[ExtResource(\"6_4koxr\")]([SubResource(\"Resource_ai5ps\"), SubResource(\"Resource_jwd6q\"), SubResource(\"Resource_22c0i\"), SubResource(\"Resource_4acdf\"), SubResource(\"Resource_8wvmf\"), SubResource(\"Resource_8p32p\"), SubResource(\"Resource_73ywc\"), SubResource(\"Resource_rleu1\"), SubResource(\"Resource_uj8l6\")])\n"
  },
  {
    "path": "guide_examples/action_priority/mapping_contexts/actions/acid_bolt.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://esf4ilpf0inv\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_867co\"]\n\n[resource]\nscript = ExtResource(\"1_867co\")\nname = &\"\"\naction_value_type = 0\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/action_priority/mapping_contexts/actions/acid_enchantment.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://bhq3gby2yiibf\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_w7kln\"]\n\n[resource]\nscript = ExtResource(\"1_w7kln\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/action_priority/mapping_contexts/actions/fire_enchantment.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://bfskfiw1k8574\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_rlep8\"]\n\n[resource]\nscript = ExtResource(\"1_rlep8\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/action_priority/mapping_contexts/actions/heal.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://dsp8h1ycwd6tt\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_o3iqb\"]\n\n[resource]\nscript = ExtResource(\"1_o3iqb\")\nname = &\"\"\naction_value_type = 0\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/action_priority/mapping_contexts/actions/healing_enchantment.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://dtr3jy86gc3rk\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_0fh74\"]\n\n[resource]\nscript = ExtResource(\"1_0fh74\")\nname = &\"\"\naction_value_type = 0\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/action_priority/mapping_contexts/actions/mana_bolt.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://cdhpb7yuq5pkb\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_ka3gg\"]\n\n[resource]\nscript = ExtResource(\"1_ka3gg\")\nname = &\"\"\naction_value_type = 0\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/action_priority/mapping_contexts/actions/mana_enchantment.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://do3hivxhwoqvi\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_4c7tt\"]\n\n[resource]\nscript = ExtResource(\"1_4c7tt\")\nname = &\"\"\naction_value_type = 0\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/action_priority/mapping_contexts/actions/shield.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://b5plj56pss47x\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_kmjgp\"]\n\n[resource]\nscript = ExtResource(\"1_kmjgp\")\nname = &\"\"\naction_value_type = 0\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/action_priority/mapping_contexts/actions/spell_toggle.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://c5eq1avod0lu8\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_m4tof\"]\n\n[resource]\nscript = ExtResource(\"1_m4tof\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = false\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/action_priority/spell_indicator/spell_indicator.gd",
    "content": "extends Node2D\n\n@export var action:GUIDEAction\n@export var texture:Texture2D\n\n@onready var _animation_player:AnimationPlayer = %AnimationPlayer\n@onready var _sprite_2d:Sprite2D = %Sprite2D\n\nfunc _ready() -> void:\n\t_sprite_2d.texture = texture\n\taction.triggered.connect(_animation_player.play.bind(\"run\"))\n"
  },
  {
    "path": "guide_examples/action_priority/spell_indicator/spell_indicator.gd.uid",
    "content": "uid://b3h0rac24v3l0\n"
  },
  {
    "path": "guide_examples/action_priority/spell_indicator/spell_indicator.tscn",
    "content": "[gd_scene load_steps=6 format=3 uid=\"uid://cx8f0hljh5dhs\"]\n\n[ext_resource type=\"Texture2D\" uid=\"uid://dbwgq8udtj2hp\" path=\"res://guide_examples/action_priority/icons/enchant-acid-3.png\" id=\"1_7l2hh\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/action_priority/spell_indicator/spell_indicator.gd\" id=\"1_hpegm\"]\n\n[sub_resource type=\"Animation\" id=\"Animation_j3vtt\"]\nlength = 0.001\ntracks/0/type = \"value\"\ntracks/0/imported = false\ntracks/0/enabled = true\ntracks/0/path = NodePath(\"Sprite2D:position\")\ntracks/0/interp = 1\ntracks/0/loop_wrap = true\ntracks/0/keys = {\n\"times\": PackedFloat32Array(0),\n\"transitions\": PackedFloat32Array(1),\n\"update\": 0,\n\"values\": [Vector2(0, 0)]\n}\ntracks/1/type = \"value\"\ntracks/1/imported = false\ntracks/1/enabled = true\ntracks/1/path = NodePath(\"Sprite2D:modulate\")\ntracks/1/interp = 1\ntracks/1/loop_wrap = true\ntracks/1/keys = {\n\"times\": PackedFloat32Array(0),\n\"transitions\": PackedFloat32Array(1),\n\"update\": 0,\n\"values\": [Color(1, 1, 1, 0)]\n}\n\n[sub_resource type=\"Animation\" id=\"Animation_4iqo1\"]\nresource_name = \"run\"\ntracks/0/type = \"value\"\ntracks/0/imported = false\ntracks/0/enabled = true\ntracks/0/path = NodePath(\"Sprite2D:position\")\ntracks/0/interp = 1\ntracks/0/loop_wrap = true\ntracks/0/keys = {\n\"times\": PackedFloat32Array(0, 1),\n\"transitions\": PackedFloat32Array(1, 1),\n\"update\": 0,\n\"values\": [Vector2(0, 0), Vector2(0, -346)]\n}\ntracks/1/type = \"value\"\ntracks/1/imported = false\ntracks/1/enabled = true\ntracks/1/path = NodePath(\"Sprite2D:modulate\")\ntracks/1/interp = 1\ntracks/1/loop_wrap = true\ntracks/1/keys = {\n\"times\": PackedFloat32Array(0, 1),\n\"transitions\": PackedFloat32Array(1, 0.176777),\n\"update\": 0,\n\"values\": [Color(1, 1, 1, 1), Color(1, 1, 1, 0)]\n}\n\n[sub_resource type=\"AnimationLibrary\" id=\"AnimationLibrary_h1go0\"]\n_data = {\n\"RESET\": SubResource(\"Animation_j3vtt\"),\n\"run\": SubResource(\"Animation_4iqo1\")\n}\n\n[node name=\"SpellIndicator\" type=\"Node2D\"]\nscript = ExtResource(\"1_hpegm\")\n\n[node name=\"Sprite2D\" type=\"Sprite2D\" parent=\".\"]\nunique_name_in_owner = true\nmodulate = Color(1, 1, 1, 0)\nscale = Vector2(0.5, 0.5)\ntexture = ExtResource(\"1_7l2hh\")\n\n[node name=\"AnimationPlayer\" type=\"AnimationPlayer\" parent=\".\"]\nunique_name_in_owner = true\nlibraries = {\n\"\": SubResource(\"AnimationLibrary_h1go0\")\n}\n"
  },
  {
    "path": "guide_examples/combine_contexts/combine_contexts.gd",
    "content": "## This example demonstrates how to combine multiple mapping contexts.\n##\n## Both keyboard and controller inputs are enabled simultaneously, and they both\n## map to the same action. This allows seamless input from multiple sources without\n## needing to switch contexts.\nextends Node2D\n\n@export var keyboard_and_mouse:GUIDEMappingContext\n@export var controller:GUIDEMappingContext\n\n\nfunc _ready() -> void:\n\t# Enable both mapping contexts at the same time.\n\t# They can both map to the same action, and whichever input is used\n\t# will control the player.\n\tGUIDE.enable_mapping_context(keyboard_and_mouse)\n\tGUIDE.enable_mapping_context(controller)\n"
  },
  {
    "path": "guide_examples/combine_contexts/combine_contexts.gd.uid",
    "content": "uid://5cis470slhau\n"
  },
  {
    "path": "guide_examples/combine_contexts/combine_contexts.tscn",
    "content": "[gd_scene load_steps=10 format=3 uid=\"uid://b3o54rxt4sg7b\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/combine_contexts/combine_contexts.gd\" id=\"1_nxm54\"]\n[ext_resource type=\"Resource\" uid=\"uid://byntrqf4ro5ul\" path=\"res://guide_examples/combine_contexts/mapping_contexts/keyboard_and_mouse.tres\" id=\"2_7en4c\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://byjxtsekdl8t2\" path=\"res://guide_examples/shared/godot_logo.svg\" id=\"2_ug15u\"]\n[ext_resource type=\"Resource\" uid=\"uid://bvg2kv4me6p18\" path=\"res://guide_examples/combine_contexts/mapping_contexts/controller.tres\" id=\"3_iacs2\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/2d_axis_mapping/player.gd\" id=\"3_w8aur\"]\n[ext_resource type=\"Resource\" uid=\"uid://bxmuoph07ha7w\" path=\"res://guide_examples/combine_contexts/mapping_contexts/move.tres\" id=\"4_7en4c\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://dkr80d2pi0d41\" path=\"res://addons/guide/debugger/guide_debugger.tscn\" id=\"7_1g1vo\"]\n[ext_resource type=\"Theme\" uid=\"uid://dot0gi1yoqmrl\" path=\"res://guide_examples/shared/ui_theme.tres\" id=\"8_qjb18\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/shared/instructions_label.gd\" id=\"9_kwp4h\"]\n\n[node name=\"CombineContexts\" type=\"Node2D\"]\nscript = ExtResource(\"1_nxm54\")\nkeyboard_and_mouse = ExtResource(\"2_7en4c\")\ncontroller = ExtResource(\"3_iacs2\")\n\n[node name=\"Player\" type=\"Node2D\" parent=\".\"]\nposition = Vector2(929, 695)\nscript = ExtResource(\"3_w8aur\")\nmove_action = ExtResource(\"4_7en4c\")\n\n[node name=\"GodotLogo\" type=\"Sprite2D\" parent=\"Player\"]\ntexture = ExtResource(\"2_ug15u\")\n\n[node name=\"UI\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"InstructionsLabel\" type=\"RichTextLabel\" parent=\"UI\"]\nunique_name_in_owner = true\ncustom_minimum_size = Vector2(500, 0)\nanchors_preset = 1\nanchor_left = 1.0\nanchor_right = 1.0\noffset_left = -552.0\noffset_top = 85.0\noffset_right = -52.0\noffset_bottom = 125.0\ngrow_horizontal = 0\ntheme = ExtResource(\"8_qjb18\")\nbbcode_enabled = true\ntext = \"Lorem Ipsum Dolor\"\nfit_content = true\nscript = ExtResource(\"9_kwp4h\")\ninstructions_text = \"%s to move.\n\nBoth keyboard and controller inputs work simultaneously!\"\nactions = Array[Resource(\"res://addons/guide/guide_action.gd\")]([ExtResource(\"4_7en4c\")])\n\n[node name=\"Debugger\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"GuideDebugger\" parent=\"Debugger\" instance=ExtResource(\"7_1g1vo\")]\ntheme = ExtResource(\"8_qjb18\")\n"
  },
  {
    "path": "guide_examples/combine_contexts/mapping_contexts/controller.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMappingContext\" load_steps=11 format=3 uid=\"uid://bvg2kv4me6p18\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action_mapping.gd\" id=\"1_g6dhr\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_mapping_context.gd\" id=\"2_k0wj4\"]\n[ext_resource type=\"Resource\" uid=\"uid://bxmuoph07ha7w\" path=\"res://guide_examples/combine_contexts/mapping_contexts/move.tres\" id=\"2_vovma\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_input_mapping.gd\" id=\"3_bs0kx\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_joy_axis_2d.gd\" id=\"4_rpogy\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_deadzone.gd\" id=\"6_bs0kx\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_8x6ir\"]\nscript = ExtResource(\"4_rpogy\")\nx = 0\ny = 1\njoy_index = -1\n\n[sub_resource type=\"Resource\" id=\"Resource_rpogy\"]\nscript = ExtResource(\"6_bs0kx\")\nlower_threshold = 0.2\nupper_threshold = 1.0\n\n[sub_resource type=\"Resource\" id=\"Resource_1mgwg\"]\nscript = ExtResource(\"3_bs0kx\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_8x6ir\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_rpogy\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_6aluo\"]\nscript = ExtResource(\"1_g6dhr\")\naction = ExtResource(\"2_vovma\")\ninput_mappings = Array[ExtResource(\"3_bs0kx\")]([SubResource(\"Resource_1mgwg\")])\n\n[resource]\nscript = ExtResource(\"2_k0wj4\")\ndisplay_name = \"\"\nmappings = Array[ExtResource(\"1_g6dhr\")]([SubResource(\"Resource_6aluo\")])\nmetadata/_custom_type_script = \"uid://dsa1dnifd6w32\"\n"
  },
  {
    "path": "guide_examples/combine_contexts/mapping_contexts/keyboard_and_mouse.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMappingContext\" load_steps=21 format=3 uid=\"uid://byntrqf4ro5ul\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action_mapping.gd\" id=\"1_h3guo\"]\n[ext_resource type=\"Resource\" uid=\"uid://bxmuoph07ha7w\" path=\"res://guide_examples/combine_contexts/mapping_contexts/move.tres\" id=\"2_vhkrg\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_input_mapping.gd\" id=\"3_8kcx6\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_key.gd\" id=\"4_grsol\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_input_swizzle.gd\" id=\"6_hcjcl\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_negate.gd\" id=\"7_icm7e\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_mapping_context.gd\" id=\"15_ybiof\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_rqldx\"]\nscript = ExtResource(\"4_grsol\")\nkey = 87\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_fedub\"]\nscript = ExtResource(\"6_hcjcl\")\norder = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_sithj\"]\nscript = ExtResource(\"7_icm7e\")\nx = true\ny = true\nz = true\n\n[sub_resource type=\"Resource\" id=\"Resource_gvylm\"]\nscript = ExtResource(\"3_8kcx6\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_rqldx\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_fedub\"), SubResource(\"Resource_sithj\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_2grck\"]\nscript = ExtResource(\"4_grsol\")\nkey = 83\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_e6ufd\"]\nscript = ExtResource(\"6_hcjcl\")\norder = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_hclrp\"]\nscript = ExtResource(\"3_8kcx6\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_2grck\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_e6ufd\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_0e6tb\"]\nscript = ExtResource(\"4_grsol\")\nkey = 65\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_o7bio\"]\nscript = ExtResource(\"7_icm7e\")\nx = true\ny = true\nz = true\n\n[sub_resource type=\"Resource\" id=\"Resource_085kd\"]\nscript = ExtResource(\"3_8kcx6\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_0e6tb\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_o7bio\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_h42bk\"]\nscript = ExtResource(\"4_grsol\")\nkey = 68\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_vnoud\"]\nscript = ExtResource(\"3_8kcx6\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_h42bk\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_x8yon\"]\nscript = ExtResource(\"1_h3guo\")\naction = ExtResource(\"2_vhkrg\")\ninput_mappings = Array[ExtResource(\"3_8kcx6\")]([SubResource(\"Resource_gvylm\"), SubResource(\"Resource_hclrp\"), SubResource(\"Resource_085kd\"), SubResource(\"Resource_vnoud\")])\n\n[resource]\nscript = ExtResource(\"15_ybiof\")\ndisplay_name = \"\"\nmappings = Array[ExtResource(\"1_h3guo\")]([SubResource(\"Resource_x8yon\")])\n"
  },
  {
    "path": "guide_examples/combine_contexts/mapping_contexts/move.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" format=3 uid=\"uid://bxmuoph07ha7w\"]\n\n[ext_resource type=\"Script\" uid=\"uid://cluhc11vixkf1\" path=\"res://addons/guide/guide_action.gd\" id=\"1_yqrp1\"]\n\n[resource]\nscript = ExtResource(\"1_yqrp1\")\naction_value_type = 2\nmetadata/_custom_type_script = \"uid://cluhc11vixkf1\"\n"
  },
  {
    "path": "guide_examples/combos/combos.gd",
    "content": "extends Node2D\n\n@export var mapping_context:GUIDEMappingContext\n\nfunc _ready() -> void:\n\tGUIDE.enable_mapping_context(mapping_context)\n\n"
  },
  {
    "path": "guide_examples/combos/combos.gd.uid",
    "content": "uid://dk0t3b28kxhf8\n"
  },
  {
    "path": "guide_examples/combos/combos.tscn",
    "content": "[gd_scene load_steps=18 format=3 uid=\"uid://b12bmbtmbuame\"]\n\n[ext_resource type=\"PackedScene\" uid=\"uid://dkr80d2pi0d41\" path=\"res://addons/guide/debugger/guide_debugger.tscn\" id=\"1_eyn1y\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/combos/combos.gd\" id=\"1_kdeb4\"]\n[ext_resource type=\"Resource\" uid=\"uid://c7uloa16ajj5p\" path=\"res://guide_examples/combos/mapping_contexts/combos.tres\" id=\"2_ahmv3\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/combos/player.gd\" id=\"3_kulxp\"]\n[ext_resource type=\"Resource\" uid=\"uid://b2wemrcnxfbmo\" path=\"res://guide_examples/combos/mapping_contexts/basic_actions/move_horizontal.tres\" id=\"4_7328w\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://byjxtsekdl8t2\" path=\"res://guide_examples/shared/godot_logo.svg\" id=\"4_uej32\"]\n[ext_resource type=\"Resource\" uid=\"uid://b0761600n8fnb\" path=\"res://guide_examples/combos/mapping_contexts/combo_actions/fireball_left.tres\" id=\"4_uvwd5\"]\n[ext_resource type=\"Resource\" uid=\"uid://dj83uxjdx6r2c\" path=\"res://guide_examples/combos/mapping_contexts/combo_actions/dash_left.tres\" id=\"5_s5wpr\"]\n[ext_resource type=\"Resource\" uid=\"uid://5ve3hevhhgnw\" path=\"res://guide_examples/combos/mapping_contexts/combo_actions/dash_right.tres\" id=\"6_vd0bg\"]\n[ext_resource type=\"Theme\" uid=\"uid://dot0gi1yoqmrl\" path=\"res://guide_examples/shared/ui_theme.tres\" id=\"11_i5q2v\"]\n[ext_resource type=\"Resource\" uid=\"uid://wdh7cg7kjul0\" path=\"res://guide_examples/combos/mapping_contexts/combo_actions/fireball_right.tres\" id=\"11_n707x\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/shared/instructions_label.gd\" id=\"11_obfhv\"]\n[ext_resource type=\"Resource\" uid=\"uid://bqiryilvj5mqv\" path=\"res://guide_examples/combos/mapping_contexts/basic_actions/move_left.tres\" id=\"12_0mhlm\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://c36cnvgv2ur60\" path=\"res://guide_examples/shared/fireball/fireball.tscn\" id=\"12_fl88r\"]\n[ext_resource type=\"Resource\" uid=\"uid://bk54ofxos3xxg\" path=\"res://guide_examples/combos/mapping_contexts/basic_actions/move_right.tres\" id=\"13_xn6qb\"]\n\n[sub_resource type=\"RectangleShape2D\" id=\"RectangleShape2D_8kkfa\"]\nsize = Vector2(137, 138)\n\n[sub_resource type=\"WorldBoundaryShape2D\" id=\"WorldBoundaryShape2D_0s5wn\"]\n\n[node name=\"Combos\" type=\"Node2D\"]\nscript = ExtResource(\"1_kdeb4\")\nmapping_context = ExtResource(\"2_ahmv3\")\n\n[node name=\"Player\" type=\"CharacterBody2D\" parent=\".\"]\nposition = Vector2(902, 841)\nscript = ExtResource(\"3_kulxp\")\ndash_speed_bonus = 400.0\nhorizontal_movement = ExtResource(\"4_7328w\")\ndash_left = ExtResource(\"5_s5wpr\")\ndash_right = ExtResource(\"6_vd0bg\")\nfireball_left = ExtResource(\"4_uvwd5\")\nfireball_right = ExtResource(\"11_n707x\")\nfireball_scene = ExtResource(\"12_fl88r\")\n\n[node name=\"Sprite\" type=\"Sprite2D\" parent=\"Player\"]\ntexture = ExtResource(\"4_uej32\")\n\n[node name=\"CollisionShape2D\" type=\"CollisionShape2D\" parent=\"Player\"]\nposition = Vector2(1.5, 1)\nshape = SubResource(\"RectangleShape2D_8kkfa\")\n\n[node name=\"Ground\" type=\"StaticBody2D\" parent=\".\"]\nposition = Vector2(-44, 954)\n\n[node name=\"ColorRect\" type=\"ColorRect\" parent=\"Ground\"]\noffset_left = -427.0\noffset_right = 2555.0\noffset_bottom = 150.0\ncolor = Color(0.285871, 0.0915713, 0.0208481, 1)\n\n[node name=\"CollisionShape2D\" type=\"CollisionShape2D\" parent=\"Ground\"]\nposition = Vector2(869, 1)\nshape = SubResource(\"WorldBoundaryShape2D_0s5wn\")\n\n[node name=\"UILayer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"InstructionsLabel\" type=\"RichTextLabel\" parent=\"UILayer\"]\nanchors_preset = 1\nanchor_left = 1.0\nanchor_right = 1.0\noffset_left = -482.0\noffset_top = 21.0\noffset_bottom = 205.0\ngrow_horizontal = 0\ntheme = ExtResource(\"11_i5q2v\")\nbbcode_enabled = true\ntext = \"Lore, ipsum dolor sit amet.\"\nfit_content = true\nscroll_active = false\nscript = ExtResource(\"11_obfhv\")\ninstructions_text = \"Press %s to move left.\nPress %s to move right.\nPress %s to dash left.\nPress %s to dash right.\nPress %s shoot a fireball to the left.\nPress %s to shoot a fireball to the right.\"\nactions = Array[Resource(\"res://addons/guide/guide_action.gd\")]([ExtResource(\"12_0mhlm\"), ExtResource(\"13_xn6qb\"), ExtResource(\"5_s5wpr\"), ExtResource(\"6_vd0bg\"), ExtResource(\"4_uvwd5\"), ExtResource(\"11_n707x\")])\n\n[node name=\"DebuggerLayer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"GuideDebugger\" parent=\"DebuggerLayer\" instance=ExtResource(\"1_eyn1y\")]\ntheme = ExtResource(\"11_i5q2v\")\nmetadata/_edit_lock_ = true\n"
  },
  {
    "path": "guide_examples/combos/mapping_contexts/basic_actions/fire.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://cgr4iegvrkebx\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_pxjqb\"]\n\n[resource]\nscript = ExtResource(\"1_pxjqb\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"Player Controls\"\n"
  },
  {
    "path": "guide_examples/combos/mapping_contexts/basic_actions/move_horizontal.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://b2wemrcnxfbmo\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_bg42k\"]\n\n[resource]\nscript = ExtResource(\"1_bg42k\")\nname = &\"\"\naction_value_type = 0\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/combos/mapping_contexts/basic_actions/move_left.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://bqiryilvj5mqv\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_pxjqb\"]\n\n[resource]\nscript = ExtResource(\"1_pxjqb\")\nname = &\"\"\naction_value_type = 0\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/combos/mapping_contexts/basic_actions/move_right.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://bk54ofxos3xxg\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_ppgom\"]\n\n[resource]\nscript = ExtResource(\"1_ppgom\")\nname = &\"\"\naction_value_type = 0\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/combos/mapping_contexts/combo_actions/dash_left.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://dj83uxjdx6r2c\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_hxh8l\"]\n\n[resource]\nscript = ExtResource(\"1_hxh8l\")\nname = &\"\"\naction_value_type = 0\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/combos/mapping_contexts/combo_actions/dash_right.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://5ve3hevhhgnw\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_cbjx7\"]\n\n[resource]\nscript = ExtResource(\"1_cbjx7\")\nname = &\"\"\naction_value_type = 0\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/combos/mapping_contexts/combo_actions/fireball_left.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://b0761600n8fnb\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_exrdu\"]\n\n[resource]\nscript = ExtResource(\"1_exrdu\")\nname = &\"\"\naction_value_type = 0\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/combos/mapping_contexts/combo_actions/fireball_right.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://wdh7cg7kjul0\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_clys2\"]\n\n[resource]\nscript = ExtResource(\"1_clys2\")\nname = &\"\"\naction_value_type = 0\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/combos/mapping_contexts/combos.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMappingContext\" load_steps=57 format=3 uid=\"uid://c7uloa16ajj5p\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_mapping_context.gd\" id=\"1_bycs2\"]\n[ext_resource type=\"Resource\" uid=\"uid://bqiryilvj5mqv\" path=\"res://guide_examples/combos/mapping_contexts/basic_actions/move_left.tres\" id=\"1_pb347\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action_mapping.gd\" id=\"2_o8ffe\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_key.gd\" id=\"2_wsx31\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_input_mapping.gd\" id=\"3_dnh2v\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_negate.gd\" id=\"3_ji605\"]\n[ext_resource type=\"Resource\" uid=\"uid://bk54ofxos3xxg\" path=\"res://guide_examples/combos/mapping_contexts/basic_actions/move_right.tres\" id=\"5_jksbs\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_action.gd\" id=\"8_04uuh\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_combo_cancel_action.gd\" id=\"8_ewrgg\"]\n[ext_resource type=\"Resource\" uid=\"uid://5ve3hevhhgnw\" path=\"res://guide_examples/combos/mapping_contexts/combo_actions/dash_right.tres\" id=\"9_ciqk8\"]\n[ext_resource type=\"Resource\" uid=\"uid://b0761600n8fnb\" path=\"res://guide_examples/combos/mapping_contexts/combo_actions/fireball_left.tres\" id=\"9_lvx6c\"]\n[ext_resource type=\"Resource\" uid=\"uid://wdh7cg7kjul0\" path=\"res://guide_examples/combos/mapping_contexts/combo_actions/fireball_right.tres\" id=\"12_icm8e\"]\n[ext_resource type=\"Resource\" uid=\"uid://b2wemrcnxfbmo\" path=\"res://guide_examples/combos/mapping_contexts/basic_actions/move_horizontal.tres\" id=\"13_u4d84\"]\n[ext_resource type=\"Resource\" uid=\"uid://cgr4iegvrkebx\" path=\"res://guide_examples/combos/mapping_contexts/basic_actions/fire.tres\" id=\"17_yw71c\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_combo.gd\" id=\"19_kw0e0\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_combo_step.gd\" id=\"20_226av\"]\n[ext_resource type=\"Resource\" uid=\"uid://dj83uxjdx6r2c\" path=\"res://guide_examples/combos/mapping_contexts/combo_actions/dash_left.tres\" id=\"21_wfbjl\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_le3gd\"]\nscript = ExtResource(\"2_wsx31\")\nkey = 65\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_jphf8\"]\nscript = ExtResource(\"3_dnh2v\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_le3gd\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\nmetadata/_guide_modifiers_collapsed = true\n\n[sub_resource type=\"Resource\" id=\"Resource_ohm7l\"]\nscript = ExtResource(\"2_o8ffe\")\naction = ExtResource(\"1_pb347\")\ninput_mappings = Array[ExtResource(\"3_dnh2v\")]([SubResource(\"Resource_jphf8\")])\nmetadata/_guide_input_mappings_collapsed = false\n\n[sub_resource type=\"Resource\" id=\"Resource_bf80h\"]\nscript = ExtResource(\"2_wsx31\")\nkey = 68\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_o2rir\"]\nscript = ExtResource(\"3_dnh2v\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_bf80h\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_3ojw4\"]\nscript = ExtResource(\"2_o8ffe\")\naction = ExtResource(\"5_jksbs\")\ninput_mappings = Array[ExtResource(\"3_dnh2v\")]([SubResource(\"Resource_o2rir\")])\nmetadata/_guide_input_mappings_collapsed = false\n\n[sub_resource type=\"Resource\" id=\"Resource_xe8bg\"]\nscript = ExtResource(\"8_04uuh\")\naction = ExtResource(\"1_pb347\")\n\n[sub_resource type=\"Resource\" id=\"Resource_tisnm\"]\nscript = ExtResource(\"3_ji605\")\nx = true\ny = true\nz = true\n\n[sub_resource type=\"Resource\" id=\"Resource_6q14n\"]\nscript = ExtResource(\"3_dnh2v\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_xe8bg\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_tisnm\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_p31qj\"]\nscript = ExtResource(\"8_04uuh\")\naction = ExtResource(\"5_jksbs\")\n\n[sub_resource type=\"Resource\" id=\"Resource_m3jub\"]\nscript = ExtResource(\"3_dnh2v\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_p31qj\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_nypfm\"]\nscript = ExtResource(\"2_o8ffe\")\naction = ExtResource(\"13_u4d84\")\ninput_mappings = Array[ExtResource(\"3_dnh2v\")]([SubResource(\"Resource_6q14n\"), SubResource(\"Resource_m3jub\")])\nmetadata/_guide_input_mappings_collapsed = false\n\n[sub_resource type=\"Resource\" id=\"Resource_rgf84\"]\nscript = ExtResource(\"2_wsx31\")\nkey = 32\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_wf4jl\"]\nscript = ExtResource(\"3_dnh2v\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_rgf84\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_qxroc\"]\nscript = ExtResource(\"2_o8ffe\")\naction = ExtResource(\"17_yw71c\")\ninput_mappings = Array[ExtResource(\"3_dnh2v\")]([SubResource(\"Resource_wf4jl\")])\nmetadata/_guide_input_mappings_collapsed = false\n\n[sub_resource type=\"Resource\" id=\"Resource_ai4v6\"]\nscript = ExtResource(\"8_ewrgg\")\naction = ExtResource(\"5_jksbs\")\ncompletion_events = 1\n\n[sub_resource type=\"Resource\" id=\"Resource_22wmg\"]\nscript = ExtResource(\"20_226av\")\naction = ExtResource(\"1_pb347\")\ncompletion_events = 16\ntime_to_actuate = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_m5xce\"]\nscript = ExtResource(\"20_226av\")\naction = ExtResource(\"1_pb347\")\ncompletion_events = 16\ntime_to_actuate = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_bc2sa\"]\nscript = ExtResource(\"19_kw0e0\")\nenable_debug_print = false\nsteps = Array[ExtResource(\"20_226av\")]([SubResource(\"Resource_22wmg\"), SubResource(\"Resource_m5xce\")])\ncancellation_actions = Array[ExtResource(\"8_ewrgg\")]([SubResource(\"Resource_ai4v6\")])\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_00r0g\"]\nscript = ExtResource(\"3_dnh2v\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_bc2sa\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_abarl\"]\nscript = ExtResource(\"2_o8ffe\")\naction = ExtResource(\"21_wfbjl\")\ninput_mappings = Array[ExtResource(\"3_dnh2v\")]([SubResource(\"Resource_00r0g\")])\nmetadata/_guide_input_mappings_collapsed = false\n\n[sub_resource type=\"Resource\" id=\"Resource_w3it8\"]\nscript = ExtResource(\"8_ewrgg\")\naction = ExtResource(\"1_pb347\")\ncompletion_events = 1\n\n[sub_resource type=\"Resource\" id=\"Resource_o1r17\"]\nscript = ExtResource(\"20_226av\")\naction = ExtResource(\"5_jksbs\")\ncompletion_events = 16\ntime_to_actuate = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_y7323\"]\nscript = ExtResource(\"20_226av\")\naction = ExtResource(\"5_jksbs\")\ncompletion_events = 16\ntime_to_actuate = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_0x6c4\"]\nscript = ExtResource(\"19_kw0e0\")\nenable_debug_print = false\nsteps = Array[ExtResource(\"20_226av\")]([SubResource(\"Resource_o1r17\"), SubResource(\"Resource_y7323\")])\ncancellation_actions = Array[ExtResource(\"8_ewrgg\")]([SubResource(\"Resource_w3it8\")])\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_wnc21\"]\nscript = ExtResource(\"3_dnh2v\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_0x6c4\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_1vsh7\"]\nscript = ExtResource(\"2_o8ffe\")\naction = ExtResource(\"9_ciqk8\")\ninput_mappings = Array[ExtResource(\"3_dnh2v\")]([SubResource(\"Resource_wnc21\")])\nmetadata/_guide_input_mappings_collapsed = false\n\n[sub_resource type=\"Resource\" id=\"Resource_cbc1w\"]\nscript = ExtResource(\"20_226av\")\naction = ExtResource(\"5_jksbs\")\ncompletion_events = 16\ntime_to_actuate = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_k4x1t\"]\nscript = ExtResource(\"20_226av\")\naction = ExtResource(\"1_pb347\")\ncompletion_events = 16\ntime_to_actuate = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_ryygs\"]\nscript = ExtResource(\"20_226av\")\naction = ExtResource(\"17_yw71c\")\ncompletion_events = 16\ntime_to_actuate = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_wocqt\"]\nscript = ExtResource(\"19_kw0e0\")\nenable_debug_print = false\nsteps = Array[ExtResource(\"20_226av\")]([SubResource(\"Resource_cbc1w\"), SubResource(\"Resource_k4x1t\"), SubResource(\"Resource_ryygs\")])\ncancellation_actions = Array[ExtResource(\"8_ewrgg\")]([])\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_v7om3\"]\nscript = ExtResource(\"3_dnh2v\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_wocqt\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_40qct\"]\nscript = ExtResource(\"2_o8ffe\")\naction = ExtResource(\"9_lvx6c\")\ninput_mappings = Array[ExtResource(\"3_dnh2v\")]([SubResource(\"Resource_v7om3\")])\nmetadata/_guide_input_mappings_collapsed = false\n\n[sub_resource type=\"Resource\" id=\"Resource_2ivmb\"]\nscript = ExtResource(\"20_226av\")\naction = ExtResource(\"1_pb347\")\ncompletion_events = 16\ntime_to_actuate = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_o88yx\"]\nscript = ExtResource(\"20_226av\")\naction = ExtResource(\"5_jksbs\")\ncompletion_events = 16\ntime_to_actuate = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_j6a01\"]\nscript = ExtResource(\"20_226av\")\naction = ExtResource(\"17_yw71c\")\ncompletion_events = 16\ntime_to_actuate = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_lonke\"]\nscript = ExtResource(\"19_kw0e0\")\nenable_debug_print = false\nsteps = Array[ExtResource(\"20_226av\")]([SubResource(\"Resource_2ivmb\"), SubResource(\"Resource_o88yx\"), SubResource(\"Resource_j6a01\")])\ncancellation_actions = Array[ExtResource(\"8_ewrgg\")]([])\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_do51u\"]\nscript = ExtResource(\"3_dnh2v\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_lonke\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_pn365\"]\nscript = ExtResource(\"2_o8ffe\")\naction = ExtResource(\"12_icm8e\")\ninput_mappings = Array[ExtResource(\"3_dnh2v\")]([SubResource(\"Resource_do51u\")])\nmetadata/_guide_input_mappings_collapsed = false\n\n[resource]\nscript = ExtResource(\"1_bycs2\")\ndisplay_name = \"\"\nmappings = Array[ExtResource(\"2_o8ffe\")]([SubResource(\"Resource_ohm7l\"), SubResource(\"Resource_3ojw4\"), SubResource(\"Resource_nypfm\"), SubResource(\"Resource_qxroc\"), SubResource(\"Resource_abarl\"), SubResource(\"Resource_1vsh7\"), SubResource(\"Resource_40qct\"), SubResource(\"Resource_pn365\")])\nmetadata/_guide_action_mappings_collapsed = false\n"
  },
  {
    "path": "guide_examples/combos/player.gd",
    "content": "## This is a somewhat more complex player example. Note how all the combos\n## are completely handled by GUIDE, the player doesn't need to know which\n## inputs trigger them. \nextends CharacterBody2D\n\n@export var speed:float = 150\n@export var dash_speed_bonus:float = 250\n\n@export var horizontal_movement:GUIDEAction\n@export var dash_left:GUIDEAction\n@export var dash_right:GUIDEAction\n@export var fireball_left:GUIDEAction\n@export var fireball_right:GUIDEAction\n\n@export var fireball_scene:PackedScene\n\nvar _dash_bonus:float \n\nfunc _ready() -> void:\n\t# We can use the event system to get notified whenever\n\t# the combo actions trigger. This way we don't need to check them\n\t# in _physics_process every frame.\n\tdash_left.triggered.connect(func() -> void: _dash_bonus = -1)\n\tdash_right.triggered.connect(func() -> void: _dash_bonus = 1)\n\tfireball_left.triggered.connect(_spawn_fireball.bind(Vector2.LEFT))\n\tfireball_right.triggered.connect(_spawn_fireball.bind(Vector2.RIGHT))\n\n\nfunc _physics_process(delta:float) -> void:\n\t# Get current left-right input\n\tvar movement:float = horizontal_movement.value_axis_1d\n\t\n\t# Move any dash bonus towards zero\n\t_dash_bonus = move_toward(_dash_bonus, 0, delta)\n\t\n\t# Calculate new velocity\n\tvelocity.x = movement * speed + _dash_bonus * dash_speed_bonus\n\tvelocity.y = 980\n\tmove_and_slide()\n\t\n\t\nfunc _spawn_fireball(direction:Vector2) -> void:\n\t# spawn a new fireball\n\tvar fireball:Node2D = fireball_scene.instantiate()\n\t# add it to the tree\n\tget_parent().add_child(fireball)\n\t# start at our position/orientation\n\tfireball.global_transform = global_transform\n\t# fly into the given direction\n\tfireball.direction = direction\n"
  },
  {
    "path": "guide_examples/combos/player.gd.uid",
    "content": "uid://bd1xrcr5qu5yd\n"
  },
  {
    "path": "guide_examples/hair_trigger/hair_trigger.gd",
    "content": "extends Node\n\n@export var mapping_context: GUIDEMappingContext\n@export var fire_action: GUIDEAction\n\n@onready var visualizer: Control = %TriggerVisualizer\n\nfunc _ready() -> void:\n\tif mapping_context:\n\t\tGUIDE.enable_mapping_context(mapping_context)\n\n\t\t# Set the action on the visualizer\n\t\tvisualizer.fire_action = fire_action\n\n\t\t# Get the duplicated trigger from GUIDE's internals\n\t\t# Triggers are duplicated when contexts are enabled, so we need the runtime instance\n\t\t# You usually do not want to do this in your game, this is just so \n\t\t# we can show the internals of GUIDE in the demo.\n\t\tfor action_mapping in GUIDE._active_action_mappings:\n\t\t\tif action_mapping.action == fire_action:\n\t\t\t\tfor input_mapping in action_mapping.input_mappings:\n\t\t\t\t\tif not input_mapping.triggers.is_empty():\n\t\t\t\t\t\tvar trigger:GUIDETrigger = input_mapping.triggers[0]\n\t\t\t\t\t\tif trigger is GUIDETriggerHair:\n\t\t\t\t\t\t\tvisualizer.hair_trigger = trigger\n\t\t\t\t\t\t\tbreak\n"
  },
  {
    "path": "guide_examples/hair_trigger/hair_trigger.gd.uid",
    "content": "uid://6612e0tp677o\n"
  },
  {
    "path": "guide_examples/hair_trigger/hair_trigger.tscn",
    "content": "[gd_scene load_steps=7 format=3 uid=\"uid://cihixy0f3l6yj\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/hair_trigger/hair_trigger.gd\" id=\"1\"]\n[ext_resource type=\"Resource\" uid=\"uid://c3dw7mq0tqbu\" path=\"res://guide_examples/hair_trigger/mapping_contexts/fire_context.tres\" id=\"2\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/hair_trigger/trigger_visualizer.gd\" id=\"3\"]\n[ext_resource type=\"Resource\" uid=\"uid://c1d2e3f4g5h6i\" path=\"res://guide_examples/hair_trigger/mapping_contexts/fire.tres\" id=\"4\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://dkr80d2pi0d41\" path=\"res://addons/guide/debugger/guide_debugger.tscn\" id=\"5\"]\n[ext_resource type=\"Theme\" uid=\"uid://dot0gi1yoqmrl\" path=\"res://guide_examples/shared/ui_theme.tres\" id=\"5_unec5\"]\n\n[node name=\"HairTrigger\" type=\"Node\"]\nscript = ExtResource(\"1\")\nmapping_context = ExtResource(\"2\")\nfire_action = ExtResource(\"4\")\n\n[node name=\"CanvasLayer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"TriggerVisualizer\" type=\"Control\" parent=\"CanvasLayer\"]\nunique_name_in_owner = true\nlayout_mode = 3\nanchors_preset = 8\nanchor_left = 0.5\nanchor_top = 0.5\nanchor_right = 0.5\nanchor_bottom = 0.5\noffset_left = -250.0\noffset_top = -250.0\noffset_right = 250.0\noffset_bottom = 250.0\ngrow_horizontal = 2\ngrow_vertical = 2\nscript = ExtResource(\"3\")\n\n[node name=\"Instructions\" type=\"Label\" parent=\"CanvasLayer\"]\nanchors_preset = 10\nanchor_right = 1.0\noffset_bottom = 80.0\ngrow_horizontal = 2\ntheme = ExtResource(\"5_unec5\")\ntext = \"Hair Trigger Example\n\nUse right trigger (RT) on controller. Fires when rising by threshold from valley.\nReleases when dropping by threshold from peak.\"\nhorizontal_alignment = 1\nvertical_alignment = 1\n\n[node name=\"GuideDebugger\" parent=\"CanvasLayer\" instance=ExtResource(\"5\")]\ntheme = ExtResource(\"5_unec5\")\n"
  },
  {
    "path": "guide_examples/hair_trigger/mapping_contexts/fire.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://c1d2e3f4g5h6i\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1\"]\n\n[resource]\nscript = ExtResource(\"1\")\nname = &\"fire\"\naction_value_type = 0\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"Fire\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/hair_trigger/mapping_contexts/fire_context.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMappingContext\" load_steps=11 format=3 uid=\"uid://c3dw7mq0tqbu\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_mapping_context.gd\" id=\"1\"]\n[ext_resource type=\"Resource\" uid=\"uid://c1d2e3f4g5h6i\" path=\"res://guide_examples/hair_trigger/mapping_contexts/fire.tres\" id=\"2\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_joy_axis_1d.gd\" id=\"3\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_input_mapping.gd\" id=\"4\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action_mapping.gd\" id=\"5\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_hair.gd\" id=\"6\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_input\"]\nscript = ExtResource(\"3\")\naxis = 5\njoy_index = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_hair_trigger\"]\nscript = ExtResource(\"6\")\nactuation_threshold = 0.3\n\n[sub_resource type=\"Resource\" id=\"Resource_input_mapping\"]\nscript = ExtResource(\"4\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_input\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_hair_trigger\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_action_mapping\"]\nscript = ExtResource(\"5\")\naction = ExtResource(\"2\")\ninput_mappings = Array[ExtResource(\"4\")]([SubResource(\"Resource_input_mapping\")])\n\n[resource]\nscript = ExtResource(\"1\")\ndisplay_name = \"Hair Trigger Example\"\nmappings = Array[ExtResource(\"5\")]([SubResource(\"Resource_action_mapping\")])\n"
  },
  {
    "path": "guide_examples/hair_trigger/trigger_visualizer.gd",
    "content": "extends Control\n\n# Constants\nconst RADIUS := 450.0\nconst CENTER := Vector2(50, 450)\nconst LINE_WIDTH := 3.0\nconst LABEL_FONT_SIZE := 21\nconst ARC_POINTS := 32\nconst DASH_LENGTH := 10.0\nconst GAP_LENGTH := 5.0\n\n# State\nvar fire_action: GUIDEAction:\n\tset(value):\n\t\tfire_action = value\n\t\tif fire_action:\n\t\t\tfire_action.triggered.connect(_on_triggered)\n\t\t\tfire_action.completed.connect(_on_completed)\n\nvar hair_trigger: GUIDETriggerHair\nvar current_value := 0.0\nvar is_triggered := false\n\n\nfunc _process(_delta: float) -> void:\n\tif fire_action:\n\t\tcurrent_value = fire_action.value_axis_1d\n\tqueue_redraw()\n\n\nfunc _draw() -> void:\n\t# Background changes color based on trigger state\n\tvar bg_color := Color.DARK_GREEN if is_triggered else Color.DARK_RED\n\t_draw_quarter_circle(bg_color)\n\t_draw_quarter_circle_outline(Color.WHITE)\n\n\t# Draw markers and labels\n\tif hair_trigger:\n\t\t_draw_edge_marker()\n\t\t_draw_threshold_marker()\n\n\t_draw_current_value_marker()\n\t_draw_status_label()\n\n\nfunc _draw_edge_marker() -> void:\n\tvar angle := hair_trigger._edge_value * 90.0\n\t_draw_radial_line(angle, Color.CYAN, LINE_WIDTH * 2)\n\t_draw_radial_label(angle, \"Edge: %.2f\" % hair_trigger._edge_value, Color.CYAN, 50)\n\n\nfunc _draw_threshold_marker() -> void:\n\t# Calculate dynamic threshold target\n\tvar offset := -hair_trigger.actuation_threshold if is_triggered else hair_trigger.actuation_threshold\n\tvar target := clampf(hair_trigger._edge_value + offset, 0.0, 1.0)\n\tvar angle := target * 90.0\n\n\t_draw_dashed_radial_line(angle, Color.YELLOW)\n\t_draw_radial_label(angle, \"Threshold: %.2f\" % target, Color.YELLOW, 50)\n\n\nfunc _draw_current_value_marker() -> void:\n\tvar angle := current_value * 90.0\n\t_draw_radial_line(angle, Color.WHITE, LINE_WIDTH * 3)\n\t_draw_radial_label(angle, \"Value: %.2f\" % current_value, Color.WHITE, 80)\n\n\nfunc _draw_status_label() -> void:\n\tvar text := \"TRIGGERED\" if is_triggered else \"NOT TRIGGERED\"\n\tvar color := Color.GREEN if is_triggered else Color.RED\n\tvar font := ThemeDB.fallback_font\n\tdraw_string(font, CENTER + Vector2(80, -30), text, HORIZONTAL_ALIGNMENT_CENTER, -1, 24, color)\n\n\nfunc _draw_quarter_circle(color: Color) -> void:\n\tvar points := PackedVector2Array([CENTER])\n\tfor i in range(ARC_POINTS + 1):\n\t\tvar angle := deg_to_rad(i * 90.0 / ARC_POINTS - 90)\n\t\tpoints.append(CENTER + Vector2(cos(angle), sin(angle)) * RADIUS)\n\tdraw_colored_polygon(points, color)\n\n\nfunc _draw_quarter_circle_outline(color: Color) -> void:\n\tvar points := PackedVector2Array()\n\tfor i in range(ARC_POINTS + 1):\n\t\tvar angle := deg_to_rad(i * 90.0 / ARC_POINTS - 90)\n\t\tpoints.append(CENTER + Vector2(cos(angle), sin(angle)) * RADIUS)\n\n\tdraw_polyline(points, color, LINE_WIDTH)\n\tdraw_line(CENTER, points[0], color, LINE_WIDTH)\n\tdraw_line(CENTER, points[-1], color, LINE_WIDTH)\n\n\nfunc _draw_radial_line(angle: float, color: Color, width: float) -> void:\n\tvar rad := deg_to_rad(angle - 90)\n\tvar end := CENTER + Vector2(cos(rad), sin(rad)) * RADIUS\n\tdraw_line(CENTER, end, color, width)\n\n\nfunc _draw_dashed_radial_line(angle: float, color: Color) -> void:\n\tvar rad := deg_to_rad(angle - 90)\n\tvar direction := Vector2(cos(rad), sin(rad))\n\tvar dist := 0.0\n\n\twhile dist < RADIUS:\n\t\tvar start := CENTER + direction * dist\n\t\tvar end := CENTER + direction * minf(dist + DASH_LENGTH, RADIUS)\n\t\tdraw_line(start, end, color, LINE_WIDTH)\n\t\tdist += DASH_LENGTH + GAP_LENGTH\n\n\nfunc _draw_radial_label(angle: float, text: String, color: Color, offset: float) -> void:\n\tvar rad := deg_to_rad(angle - 90)\n\tvar pos := CENTER + Vector2(cos(rad), sin(rad)) * (RADIUS + offset)\n\tvar font := ThemeDB.fallback_font\n\tdraw_string(font, pos, text, HORIZONTAL_ALIGNMENT_CENTER, -1, LABEL_FONT_SIZE, color)\n\n\nfunc _on_triggered() -> void:\n\tis_triggered = true\n\n\nfunc _on_completed() -> void:\n\tis_triggered = false\n"
  },
  {
    "path": "guide_examples/hair_trigger/trigger_visualizer.gd.uid",
    "content": "uid://fkqhqjnokjifb\n"
  },
  {
    "path": "guide_examples/input_contexts/boat.gd",
    "content": "extends CharacterBody2D\n\nsignal exited()\n@export var speed:float = 300\n@export var turn_speed_degrees:float = 180\n\n@export var context:GUIDEMappingContext\n@export var accelerate:GUIDEAction\n@export var turn:GUIDEAction\n@export var leave:GUIDEAction\n\n\n@onready var _player_spot:Node2D = %PlayerSpot\n@onready var _exit_spot:Node2D = %ExitSpot\n\nvar _player:Node2D\n\nfunc _ready() -> void:\n\tleave.triggered.connect(_on_leave)\n\n\nfunc _physics_process(delta:float) -> void:\n\t# rotate by our turn axis\n\trotate(turn.value_axis_1d * deg_to_rad(turn_speed_degrees) * delta)\n\t# accelerate by our acceleration axis\n\tvelocity = transform.x * accelerate.value_axis_1d * speed\n\tmove_and_slide()\n\t\n\nfunc enter(player:Node2D) -> void:\n\t# Move the player to the player spot\n\t_player = player\n\tplayer.reparent(_player_spot, false)\n\t_player.position = Vector2.ZERO\n\t\n\t# And enable the boat controls\n\tGUIDE.enable_mapping_context(context)\n\t\n\nfunc _on_leave() -> void:\n\t# Disable boat controls\n\tGUIDE.disable_mapping_context(context)\n\t\n\t# put player back in the world\n\t_player.reparent(get_parent(), false)\n\t_player.global_position = _exit_spot.global_position\n\t\n\t# this is to prevent the physics engine from going crazy when moving\n\t# the player's body\n\tawait get_tree().physics_frame\n\t\n\t# notify any interested parties that the player has exited\n\texited.emit()\n\t\n\t\n\n\t\n\t\n"
  },
  {
    "path": "guide_examples/input_contexts/boat.gd.uid",
    "content": "uid://bkggahcvec2hd\n"
  },
  {
    "path": "guide_examples/input_contexts/boat.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cyqlk5nkvswx7\"\npath=\"res://.godot/imported/boat.svg-547042152e7d4e4afdfc306682d6e571.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://guide_examples/input_contexts/boat.svg\"\ndest_files=[\"res://.godot/imported/boat.svg-547042152e7d4e4afdfc306682d6e571.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=false\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "guide_examples/input_contexts/input_contexts.gd",
    "content": "extends Node2D\n\n@export var starting_context:GUIDEMappingContext\n\nfunc _ready() -> void:\n\tGUIDE.enable_mapping_context(starting_context)\n"
  },
  {
    "path": "guide_examples/input_contexts/input_contexts.gd.uid",
    "content": "uid://cnf3xx5jxiu8q\n"
  },
  {
    "path": "guide_examples/input_contexts/input_contexts.tscn",
    "content": "[gd_scene load_steps=25 format=3 uid=\"uid://b6h4wnjfjs70m\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/input_contexts/boat.gd\" id=\"1_61cdj\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/input_contexts/input_contexts.gd\" id=\"1_386pq\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://byjxtsekdl8t2\" path=\"res://guide_examples/shared/godot_logo.svg\" id=\"1_x61i0\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://cyqlk5nkvswx7\" path=\"res://guide_examples/input_contexts/boat.svg\" id=\"1_yfaid\"]\n[ext_resource type=\"Resource\" uid=\"uid://bv3t73wg3atf7\" path=\"res://guide_examples/input_contexts/mapping_contexts/boat_context.tres\" id=\"2_ha2ml\"]\n[ext_resource type=\"Resource\" uid=\"uid://5jercxe6t3go\" path=\"res://guide_examples/input_contexts/mapping_contexts/boat_actions/accelerate.tres\" id=\"3_8s4br\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/input_contexts/player.gd\" id=\"3_kn2qk\"]\n[ext_resource type=\"Resource\" uid=\"uid://cplpvxhus6bwb\" path=\"res://guide_examples/input_contexts/mapping_contexts/player_context.tres\" id=\"4_3xwjv\"]\n[ext_resource type=\"Resource\" uid=\"uid://qsysw0ljlj0l\" path=\"res://guide_examples/input_contexts/mapping_contexts/boat_actions/turn.tres\" id=\"4_w1gps\"]\n[ext_resource type=\"Resource\" uid=\"uid://cnaj42xnfcibo\" path=\"res://guide_examples/input_contexts/mapping_contexts/player_actions/move.tres\" id=\"5_70jqj\"]\n[ext_resource type=\"Resource\" uid=\"uid://bk2j1ww7iwqd0\" path=\"res://guide_examples/input_contexts/mapping_contexts/boat_actions/leave.tres\" id=\"5_typxl\"]\n[ext_resource type=\"Resource\" uid=\"uid://crjkk2edn8g8k\" path=\"res://guide_examples/input_contexts/mapping_contexts/player_actions/use.tres\" id=\"6_aiqns\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://dkr80d2pi0d41\" path=\"res://addons/guide/debugger/guide_debugger.tscn\" id=\"12_jcoq7\"]\n[ext_resource type=\"Theme\" uid=\"uid://dot0gi1yoqmrl\" path=\"res://guide_examples/shared/ui_theme.tres\" id=\"12_u0g3a\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/shared/instructions_label.gd\" id=\"14_ui0u7\"]\n\n[sub_resource type=\"RectangleShape2D\" id=\"RectangleShape2D_w57h4\"]\nsize = Vector2(1972, 59)\n\n[sub_resource type=\"RectangleShape2D\" id=\"RectangleShape2D_r5hqg\"]\nsize = Vector2(59, 1161)\n\n[sub_resource type=\"RectangleShape2D\" id=\"RectangleShape2D_ifvju\"]\nsize = Vector2(2030.5, 60.5)\n\n[sub_resource type=\"RectangleShape2D\" id=\"RectangleShape2D_qarqo\"]\nsize = Vector2(102, 1160.5)\n\n[sub_resource type=\"RectangleShape2D\" id=\"RectangleShape2D_la1oy\"]\nsize = Vector2(446.5, 1141)\n\n[sub_resource type=\"RectangleShape2D\" id=\"RectangleShape2D_3q8sb\"]\nsize = Vector2(368, 148)\n\n[sub_resource type=\"CapsuleShape2D\" id=\"CapsuleShape2D_54ta5\"]\nradius = 75.0\nheight = 252.0\n\n[sub_resource type=\"RectangleShape2D\" id=\"RectangleShape2D_3lf7l\"]\nsize = Vector2(126, 130)\n\n[sub_resource type=\"CircleShape2D\" id=\"CircleShape2D_dt2nf\"]\nradius = 141.891\n\n[node name=\"InputContexts\" type=\"Node2D\"]\nscript = ExtResource(\"1_386pq\")\nstarting_context = ExtResource(\"4_3xwjv\")\n\n[node name=\"World\" type=\"Node2D\" parent=\".\"]\n\n[node name=\"Sea\" type=\"ColorRect\" parent=\"World\"]\noffset_right = 2009.0\noffset_bottom = 1129.0\ncolor = Color(0.0440738, 0.000205037, 0.549847, 1)\nmetadata/_edit_lock_ = true\n\n[node name=\"Land\" type=\"Polygon2D\" parent=\"World\"]\ncolor = Color(0.336331, 0.394587, 0.063959, 1)\npolygon = PackedVector2Array(55, -51, 259, -24, 398, 124, 356, 225, 279, 461, 394, 656, 412, 865, 342, 1085, -15, 1119, -22, -67)\nmetadata/_edit_lock_ = true\n\n[node name=\"Jetty\" type=\"ColorRect\" parent=\"World/Land\"]\noffset_left = 283.0\noffset_top = 144.0\noffset_right = 641.0\noffset_bottom = 280.0\ncolor = Color(0.243329, 0.15798, 7.21961e-08, 1)\n\n[node name=\"World Boundaries\" type=\"StaticBody2D\" parent=\"World\"]\n\n[node name=\"CollisionShape2D\" type=\"CollisionShape2D\" parent=\"World/World Boundaries\"]\nposition = Vector2(976, -19.5)\nshape = SubResource(\"RectangleShape2D_w57h4\")\n\n[node name=\"CollisionShape2D2\" type=\"CollisionShape2D\" parent=\"World/World Boundaries\"]\nposition = Vector2(-10, 532)\nshape = SubResource(\"RectangleShape2D_r5hqg\")\n\n[node name=\"CollisionShape2D3\" type=\"CollisionShape2D\" parent=\"World/World Boundaries\"]\nposition = Vector2(975.75, 1082.25)\nshape = SubResource(\"RectangleShape2D_ifvju\")\n\n[node name=\"CollisionShape2D4\" type=\"CollisionShape2D\" parent=\"World/World Boundaries\"]\nposition = Vector2(1940, 532.25)\nshape = SubResource(\"RectangleShape2D_qarqo\")\n\n[node name=\"NoBoatZone\" type=\"StaticBody2D\" parent=\"World\"]\ncollision_layer = 2\ncollision_mask = 0\n\n[node name=\"CollisionShape2D\" type=\"CollisionShape2D\" parent=\"World/NoBoatZone\"]\nposition = Vector2(210.75, 556.5)\nshape = SubResource(\"RectangleShape2D_la1oy\")\n\n[node name=\"CollisionShape2D2\" type=\"CollisionShape2D\" parent=\"World/NoBoatZone\"]\nposition = Vector2(454, 211)\nshape = SubResource(\"RectangleShape2D_3q8sb\")\n\n[node name=\"Boat\" type=\"CharacterBody2D\" parent=\".\"]\nposition = Vector2(744, 269)\nrotation = -1.44336\ncollision_layer = 5\ncollision_mask = 3\nscript = ExtResource(\"1_61cdj\")\nspeed = 500.0\ncontext = ExtResource(\"2_ha2ml\")\naccelerate = ExtResource(\"3_8s4br\")\nturn = ExtResource(\"4_w1gps\")\nleave = ExtResource(\"5_typxl\")\n\n[node name=\"Boat\" type=\"Sprite2D\" parent=\"Boat\"]\ntexture = ExtResource(\"1_yfaid\")\n\n[node name=\"CollisionShape2D\" type=\"CollisionShape2D\" parent=\"Boat\"]\nposition = Vector2(-13, 0)\nrotation = -1.57573\nshape = SubResource(\"CapsuleShape2D_54ta5\")\n\n[node name=\"PlayerSpot\" type=\"Node2D\" parent=\"Boat\"]\nunique_name_in_owner = true\nrotation = 1.5708\nscale = Vector2(0.8, 0.8)\n\n[node name=\"ExitSpot\" type=\"Marker2D\" parent=\"Boat\"]\nunique_name_in_owner = true\nposition = Vector2(-11.0732, -212.314)\n\n[node name=\"Player\" type=\"CharacterBody2D\" parent=\".\"]\nposition = Vector2(205, 212)\nscript = ExtResource(\"3_kn2qk\")\ncontext = ExtResource(\"4_3xwjv\")\nmove = ExtResource(\"5_70jqj\")\nuse = ExtResource(\"6_aiqns\")\n\n[node name=\"Icon\" type=\"Sprite2D\" parent=\"Player\"]\ntexture = ExtResource(\"1_x61i0\")\n\n[node name=\"CollisionShape\" type=\"CollisionShape2D\" parent=\"Player\"]\nunique_name_in_owner = true\nposition = Vector2(-1, 1)\nshape = SubResource(\"RectangleShape2D_3lf7l\")\n\n[node name=\"DetectionArea\" type=\"Area2D\" parent=\"Player\"]\nunique_name_in_owner = true\ncollision_layer = 0\ncollision_mask = 4\n\n[node name=\"CollisionShape2D\" type=\"CollisionShape2D\" parent=\"Player/DetectionArea\"]\nshape = SubResource(\"CircleShape2D_dt2nf\")\n\n[node name=\"UILayer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"Label\" type=\"Label\" parent=\"UILayer\"]\noffset_left = 894.0\noffset_top = 24.0\noffset_right = 1872.0\noffset_bottom = 132.0\ntheme = ExtResource(\"12_u0g3a\")\ntext = \"This demonstrates the use of multiple mapping contexts. We have one for the player\nand one for the boat. When the player enters the boat, the boat mappings will\nbecome active and will become inactive once the player leaves. \"\n\n[node name=\"BoatInstructions\" type=\"RichTextLabel\" parent=\"UILayer\"]\noffset_left = 1316.0\noffset_top = 772.0\noffset_right = 1356.0\noffset_bottom = 812.0\ntheme = ExtResource(\"12_u0g3a\")\nscript = ExtResource(\"14_ui0u7\")\ninstructions_text = \"%s to accelerate/break.\n%s to turn the boat.\n%s to leave the boat.\"\nactions = Array[Resource(\"res://addons/guide/guide_action.gd\")]([ExtResource(\"3_8s4br\"), ExtResource(\"4_w1gps\"), ExtResource(\"5_typxl\")])\nlimit_to_context = ExtResource(\"2_ha2ml\")\n\n[node name=\"PlayerInstructions\" type=\"RichTextLabel\" parent=\"UILayer\"]\noffset_left = 1316.0\noffset_top = 772.0\noffset_right = 1356.0\noffset_bottom = 812.0\ntheme = ExtResource(\"12_u0g3a\")\nscript = ExtResource(\"14_ui0u7\")\ninstructions_text = \"%s to move.\n%s to enter the boat.\n\"\nactions = Array[Resource(\"res://addons/guide/guide_action.gd\")]([ExtResource(\"5_70jqj\"), ExtResource(\"6_aiqns\")])\nlimit_to_context = ExtResource(\"4_3xwjv\")\n\n[node name=\"DebugLayer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"GuideDebugger\" parent=\"DebugLayer\" instance=ExtResource(\"12_jcoq7\")]\ntheme = ExtResource(\"12_u0g3a\")\n"
  },
  {
    "path": "guide_examples/input_contexts/mapping_contexts/boat_actions/accelerate.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://5jercxe6t3go\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_tkn2p\"]\n\n[resource]\nscript = ExtResource(\"1_tkn2p\")\nname = &\"\"\naction_value_type = 1\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/input_contexts/mapping_contexts/boat_actions/leave.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://bk2j1ww7iwqd0\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_3d3m7\"]\n\n[resource]\nscript = ExtResource(\"1_3d3m7\")\nname = &\"\"\naction_value_type = 0\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/input_contexts/mapping_contexts/boat_actions/turn.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://qsysw0ljlj0l\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_4gxp2\"]\n\n[resource]\nscript = ExtResource(\"1_4gxp2\")\nname = &\"\"\naction_value_type = 1\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/input_contexts/mapping_contexts/boat_context.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMappingContext\" load_steps=26 format=3 uid=\"uid://bv3t73wg3atf7\"]\n\n[ext_resource type=\"Resource\" uid=\"uid://qsysw0ljlj0l\" path=\"res://guide_examples/input_contexts/mapping_contexts/boat_actions/turn.tres\" id=\"1_ovglr\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_key.gd\" id=\"2_0hduu\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_negate.gd\" id=\"3_jicb2\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_input_mapping.gd\" id=\"4_ymfat\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action_mapping.gd\" id=\"5_3quxn\"]\n[ext_resource type=\"Resource\" uid=\"uid://5jercxe6t3go\" path=\"res://guide_examples/input_contexts/mapping_contexts/boat_actions/accelerate.tres\" id=\"6_pocgd\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_mapping_context.gd\" id=\"7_1yt57\"]\n[ext_resource type=\"Resource\" uid=\"uid://bk2j1ww7iwqd0\" path=\"res://guide_examples/input_contexts/mapping_contexts/boat_actions/leave.tres\" id=\"7_t38lc\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_pressed.gd\" id=\"8_ise0o\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_2l73a\"]\nscript = ExtResource(\"2_0hduu\")\nkey = 87\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_p26ag\"]\nscript = ExtResource(\"4_ymfat\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_2l73a\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_fndx2\"]\nscript = ExtResource(\"2_0hduu\")\nkey = 83\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_8c6p8\"]\nscript = ExtResource(\"3_jicb2\")\nx = true\ny = true\nz = true\n\n[sub_resource type=\"Resource\" id=\"Resource_7ubh7\"]\nscript = ExtResource(\"4_ymfat\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_fndx2\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_8c6p8\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_yd1l1\"]\nscript = ExtResource(\"5_3quxn\")\naction = ExtResource(\"6_pocgd\")\ninput_mappings = Array[ExtResource(\"4_ymfat\")]([SubResource(\"Resource_p26ag\"), SubResource(\"Resource_7ubh7\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_bw7vh\"]\nscript = ExtResource(\"2_0hduu\")\nkey = 65\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_55txo\"]\nscript = ExtResource(\"3_jicb2\")\nx = true\ny = true\nz = true\n\n[sub_resource type=\"Resource\" id=\"Resource_4pm2b\"]\nscript = ExtResource(\"4_ymfat\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_bw7vh\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_55txo\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_mathf\"]\nscript = ExtResource(\"2_0hduu\")\nkey = 68\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_j5i1b\"]\nscript = ExtResource(\"4_ymfat\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_mathf\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_siw8f\"]\nscript = ExtResource(\"5_3quxn\")\naction = ExtResource(\"1_ovglr\")\ninput_mappings = Array[ExtResource(\"4_ymfat\")]([SubResource(\"Resource_4pm2b\"), SubResource(\"Resource_j5i1b\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_xiqqo\"]\nscript = ExtResource(\"2_0hduu\")\nkey = 69\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_16uvn\"]\nscript = ExtResource(\"8_ise0o\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_8047g\"]\nscript = ExtResource(\"4_ymfat\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_xiqqo\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_16uvn\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_npqbc\"]\nscript = ExtResource(\"5_3quxn\")\naction = ExtResource(\"7_t38lc\")\ninput_mappings = Array[ExtResource(\"4_ymfat\")]([SubResource(\"Resource_8047g\")])\n\n[resource]\nscript = ExtResource(\"7_1yt57\")\ndisplay_name = \"\"\nmappings = Array[ExtResource(\"5_3quxn\")]([SubResource(\"Resource_yd1l1\"), SubResource(\"Resource_siw8f\"), SubResource(\"Resource_npqbc\")])\n"
  },
  {
    "path": "guide_examples/input_contexts/mapping_contexts/player_actions/move.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://cnaj42xnfcibo\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_amhrr\"]\n\n[resource]\nscript = ExtResource(\"1_amhrr\")\nname = &\"\"\naction_value_type = 2\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/input_contexts/mapping_contexts/player_actions/use.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://crjkk2edn8g8k\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_7lwep\"]\n\n[resource]\nscript = ExtResource(\"1_7lwep\")\nname = &\"\"\naction_value_type = 0\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/input_contexts/mapping_contexts/player_context.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMappingContext\" load_steps=27 format=3 uid=\"uid://cplpvxhus6bwb\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_mapping_context.gd\" id=\"1_126cd\"]\n[ext_resource type=\"Resource\" uid=\"uid://cnaj42xnfcibo\" path=\"res://guide_examples/input_contexts/mapping_contexts/player_actions/move.tres\" id=\"1_hm3wk\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_key.gd\" id=\"2_xomf3\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_input_swizzle.gd\" id=\"3_0ask7\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_negate.gd\" id=\"4_07e03\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_input_mapping.gd\" id=\"5_1myws\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action_mapping.gd\" id=\"6_h3mfx\"]\n[ext_resource type=\"Resource\" uid=\"uid://crjkk2edn8g8k\" path=\"res://guide_examples/input_contexts/mapping_contexts/player_actions/use.tres\" id=\"7_dx1om\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_pressed.gd\" id=\"8_lmv6n\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_5yf1p\"]\nscript = ExtResource(\"2_xomf3\")\nkey = 87\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = false\n\n[sub_resource type=\"Resource\" id=\"Resource_vo6fb\"]\nscript = ExtResource(\"3_0ask7\")\norder = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_ixhgx\"]\nscript = ExtResource(\"4_07e03\")\nx = true\ny = true\nz = true\n\n[sub_resource type=\"Resource\" id=\"Resource_pia7e\"]\nscript = ExtResource(\"5_1myws\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_5yf1p\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_vo6fb\"), SubResource(\"Resource_ixhgx\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_a11mt\"]\nscript = ExtResource(\"2_xomf3\")\nkey = 83\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = false\n\n[sub_resource type=\"Resource\" id=\"Resource_6ecpg\"]\nscript = ExtResource(\"3_0ask7\")\norder = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_s1oiy\"]\nscript = ExtResource(\"5_1myws\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_a11mt\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_6ecpg\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_m84eo\"]\nscript = ExtResource(\"2_xomf3\")\nkey = 65\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = false\n\n[sub_resource type=\"Resource\" id=\"Resource_qn63o\"]\nscript = ExtResource(\"4_07e03\")\nx = true\ny = true\nz = true\n\n[sub_resource type=\"Resource\" id=\"Resource_4dh7v\"]\nscript = ExtResource(\"5_1myws\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_m84eo\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_qn63o\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_njvt5\"]\nscript = ExtResource(\"2_xomf3\")\nkey = 68\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = false\n\n[sub_resource type=\"Resource\" id=\"Resource_hvhr4\"]\nscript = ExtResource(\"5_1myws\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_njvt5\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_o0rtq\"]\nscript = ExtResource(\"6_h3mfx\")\naction = ExtResource(\"1_hm3wk\")\ninput_mappings = Array[ExtResource(\"5_1myws\")]([SubResource(\"Resource_pia7e\"), SubResource(\"Resource_s1oiy\"), SubResource(\"Resource_4dh7v\"), SubResource(\"Resource_hvhr4\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_t3oa4\"]\nscript = ExtResource(\"2_xomf3\")\nkey = 69\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = false\n\n[sub_resource type=\"Resource\" id=\"Resource_jcwnv\"]\nscript = ExtResource(\"8_lmv6n\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_o528y\"]\nscript = ExtResource(\"5_1myws\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_t3oa4\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_jcwnv\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_0a33p\"]\nscript = ExtResource(\"6_h3mfx\")\naction = ExtResource(\"7_dx1om\")\ninput_mappings = Array[ExtResource(\"5_1myws\")]([SubResource(\"Resource_o528y\")])\n\n[resource]\nscript = ExtResource(\"1_126cd\")\ndisplay_name = \"\"\nmappings = Array[ExtResource(\"6_h3mfx\")]([SubResource(\"Resource_o0rtq\"), SubResource(\"Resource_0a33p\")])\n"
  },
  {
    "path": "guide_examples/input_contexts/player.gd",
    "content": "extends CharacterBody2D\n\n@export var context:GUIDEMappingContext\n@export var move:GUIDEAction\n@export var use:GUIDEAction\n\n@export var speed:float = 300\n\n@onready var _detection_area:Area2D = %DetectionArea\n@onready var _collision_shape:CollisionShape2D = %CollisionShape\n\nfunc _ready() -> void:\n\tuse.triggered.connect(_enter_boat)\n\t\nfunc _physics_process(_delta:float) -> void:\n\tvelocity = move.value_axis_2d.normalized() * speed\n\tmove_and_slide()\t\n\t\n\t\nfunc _enter_boat() -> void:\n\tvar boats := _detection_area.get_overlapping_bodies()\n\tif boats.is_empty():\n\t\treturn\n\t\n\t# Disable player input while in the boat\n\tGUIDE.disable_mapping_context(context)\t\n\t\n\t# disable our own collisions while in the boat\n\t_collision_shape.set_deferred(\"disabled\", true)\n\t\n\t# enter the boat\n\tboats[0].enter(self)\n\tboats[0].exited.connect(_boat_exited, CONNECT_ONE_SHOT)\t\n\n\nfunc _boat_exited() -> void:\n\t# re-enable our own mapping context\n\tGUIDE.enable_mapping_context(context)\n\t\n\t# and re-enable our collisions\n\t_collision_shape.set_deferred(\"disabled\", false)\n\t\n"
  },
  {
    "path": "guide_examples/input_contexts/player.gd.uid",
    "content": "uid://bc00xf6yb6mw0\n"
  },
  {
    "path": "guide_examples/input_prompts/input_prompts.gd",
    "content": "extends Node2D\n\nconst InstructionsLabel = preload(\"../shared/instructions_label.gd\")\nconst DeviceType = GUIDEInput.DeviceType\nconst JoyRendering = GUIDEInputFormattingOptions.JoyRendering\nconst JoyType = GUIDEInputFormattingOptions.JoyType\n\n@export var mapping_context:GUIDEMappingContext\n@export var fire:GUIDEAction\n@export var controller_activated:GUIDEAction\n@export var mouse_activated:GUIDEAction\n@export var keyboard_activated:GUIDEAction\n\n@onready var label: InstructionsLabel = %Label\n@onready var gpu_particles_2d: GPUParticles2D = %GPUParticles2D\n\nvar _render_all_devices:bool = true\nvar _current_device_type:DeviceType = DeviceType.MOUSE \nvar _formatter:GUIDEInputFormatter\n\nfunc _ready() -> void:\n\tGUIDE.enable_mapping_context(mapping_context)\n\t# get the formatter from the label. technically a hack, but this is just an example\n\t# on how to use the formatter\n\t_formatter = label._formatter\n\tlabel._update_instructions()\n\t\n\tcontroller_activated.triggered.connect(_on_device_activated.bind(DeviceType.JOY))\n\tmouse_activated.triggered.connect(_on_device_activated.bind(DeviceType.MOUSE))\n\tkeyboard_activated.triggered.connect(_on_device_activated.bind(DeviceType.KEYBOARD))\n\tfire.triggered.connect(func() -> void: gpu_particles_2d.emitting = true)\n\tfire.completed.connect(func() -> void: gpu_particles_2d.emitting = false)\n\t\n\t\n## Called when the device selection is changed. Shows either input for all devices\n## or the last activated device, depending on selection.\nfunc _on_device_selection_item_selected(index: int) -> void:\n\t# option button only has 2 entries, so if it's the second one we limit the \n\t# device type\n\tif index == 0:\n\t\t_formatter.formatting_options.input_filter = GUIDEInputFormattingOptions.INPUT_FILTER_SHOW_ALL\n\t\t_render_all_devices = true\n\telse:\n\t\t_formatter.formatting_options.input_filter = _filter_current_device_type\n\t\t_render_all_devices = false\n\tlabel._update_instructions()\n\n## Called when the controller type is changed. Shows either detected controller icons or\n## forced controller icons depending on selection.\nfunc _on_controller_type_override_item_selected(index: int) -> void:\n\tmatch index:\n\t\t0:\n\t\t\t_formatter.formatting_options.joy_rendering = JoyRendering.DEFAULT\n\t\t1:\n\t\t\t_formatter.formatting_options.joy_rendering = JoyRendering.FORCE_JOY_TYPE\n\t\t\t_formatter.formatting_options.preferred_joy_type = JoyType.MICROSOFT_CONTROLLER\n\t\t2:\n\t\t\t_formatter.formatting_options.joy_rendering = JoyRendering.FORCE_JOY_TYPE\n\t\t\t_formatter.formatting_options.preferred_joy_type = JoyType.NINTENDO_CONTROLLER\n\t\t3:\n\t\t\t_formatter.formatting_options.joy_rendering = JoyRendering.FORCE_JOY_TYPE\n\t\t\t_formatter.formatting_options.preferred_joy_type = JoyType.SONY_CONTROLLER\n\t\t4:\n\t\t\t_formatter.formatting_options.joy_rendering = JoyRendering.FORCE_JOY_TYPE\n\t\t\t_formatter.formatting_options.preferred_joy_type = JoyType.STEAM_DECK_CONTROLLER\t\t\n\tlabel._update_instructions()\n\t\t\t\n\t\t\t\n## Called when a certain device is activated. Depending on the mode\n## updates the rendering to only show the last activated device.\t\nfunc _on_device_activated(type:DeviceType) -> void:\n\t_current_device_type = type\n\tif not _render_all_devices:\n\t\t_formatter.formatting_options.input_filter = _filter_current_device_type\n\t\n\tlabel._update_instructions()\n\n## Filter function which filters the input and only shows input from the current\n## device type.\nfunc _filter_current_device_type(context:GUIDEInputFormatter.FormattingContext) -> bool:\n\t# check if there is an overlap between the input device type and the current device type\n\treturn context.input.device_type & _current_device_type > 0\n"
  },
  {
    "path": "guide_examples/input_prompts/input_prompts.gd.uid",
    "content": "uid://b0jhbgcl22st4\n"
  },
  {
    "path": "guide_examples/input_prompts/input_prompts.tscn",
    "content": "[gd_scene load_steps=14 format=3 uid=\"uid://u21n04y2xftp\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/input_prompts/input_prompts.gd\" id=\"1_ogapc\"]\n[ext_resource type=\"Resource\" uid=\"uid://b5s7df53wnarw\" path=\"res://guide_examples/input_prompts/mapping_contexts/input_prompts.tres\" id=\"2_drrnj\"]\n[ext_resource type=\"Resource\" uid=\"uid://bxa8jsi2dbtw4\" path=\"res://guide_examples/input_prompts/mapping_contexts/fire.tres\" id=\"3_gessh\"]\n[ext_resource type=\"Resource\" uid=\"uid://dbjyxldjlm3yw\" path=\"res://guide_examples/input_prompts/mapping_contexts/controller_activated.tres\" id=\"4_hasd2\"]\n[ext_resource type=\"Resource\" uid=\"uid://bpnyp22tg2ccv\" path=\"res://guide_examples/input_prompts/mapping_contexts/mouse_activated.tres\" id=\"5_dndl7\"]\n[ext_resource type=\"Resource\" uid=\"uid://dbf0wsbnpx3u8\" path=\"res://guide_examples/input_prompts/mapping_contexts/keyboard_activated.tres\" id=\"6_ofh7e\"]\n[ext_resource type=\"Theme\" uid=\"uid://dot0gi1yoqmrl\" path=\"res://guide_examples/shared/ui_theme.tres\" id=\"7_li4sj\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/shared/instructions_label.gd\" id=\"8_i8f53\"]\n\n[sub_resource type=\"Gradient\" id=\"Gradient_j5prp\"]\noffsets = PackedFloat32Array(0, 0.184375, 0.496875, 1)\ncolors = PackedColorArray(1, 1, 1, 1, 1, 1, 0.603922, 1, 1, 0.370487, 0.325751, 0.623529, 0, 0, 0, 0)\n\n[sub_resource type=\"GradientTexture1D\" id=\"GradientTexture1D_d1csl\"]\ngradient = SubResource(\"Gradient_j5prp\")\n\n[sub_resource type=\"Curve\" id=\"Curve_3lhls\"]\nmax_value = 5.0\n_data = [Vector2(0, 0.449663), 0.0, 0.0, 0, 0, Vector2(1, 2.46539), 0.0, 0.0, 0, 0]\npoint_count = 2\n\n[sub_resource type=\"CurveTexture\" id=\"CurveTexture_rgacx\"]\ncurve = SubResource(\"Curve_3lhls\")\n\n[sub_resource type=\"ParticleProcessMaterial\" id=\"ParticleProcessMaterial_1unui\"]\nparticle_flag_disable_z = true\nangle_max = 360.0\nradial_velocity_min = 37.04\nradial_velocity_max = 185.19\ngravity = Vector3(0, 98, 0)\nscale_over_velocity_min = 1.0\nscale_over_velocity_max = 2.0\nscale_over_velocity_curve = SubResource(\"CurveTexture_rgacx\")\ncolor_ramp = SubResource(\"GradientTexture1D_d1csl\")\n\n[node name=\"InputPrompts\" type=\"Node2D\"]\nscript = ExtResource(\"1_ogapc\")\nmapping_context = ExtResource(\"2_drrnj\")\nfire = ExtResource(\"3_gessh\")\ncontroller_activated = ExtResource(\"4_hasd2\")\nmouse_activated = ExtResource(\"5_dndl7\")\nkeyboard_activated = ExtResource(\"6_ofh7e\")\n\n[node name=\"GPUParticles2D\" type=\"GPUParticles2D\" parent=\".\"]\nunique_name_in_owner = true\nposition = Vector2(939, 335)\nemitting = false\namount = 400\nprocess_material = SubResource(\"ParticleProcessMaterial_1unui\")\n\n[node name=\"CanvasLayer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"MarginContainer\" type=\"MarginContainer\" parent=\"CanvasLayer\"]\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\ntheme_override_constants/margin_left = 40\ntheme_override_constants/margin_top = 40\ntheme_override_constants/margin_right = 40\ntheme_override_constants/margin_bottom = 40\n\n[node name=\"VBoxContainer\" type=\"VBoxContainer\" parent=\"CanvasLayer/MarginContainer\"]\nlayout_mode = 2\n\n[node name=\"HBoxContainer\" type=\"HBoxContainer\" parent=\"CanvasLayer/MarginContainer/VBoxContainer\"]\nlayout_mode = 2\n\n[node name=\"Spacer2\" type=\"Control\" parent=\"CanvasLayer/MarginContainer/VBoxContainer/HBoxContainer\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\n\n[node name=\"Label\" type=\"Label\" parent=\"CanvasLayer/MarginContainer/VBoxContainer/HBoxContainer\"]\ncustom_minimum_size = Vector2(800, 0)\nlayout_mode = 2\ntheme = ExtResource(\"7_li4sj\")\ntext = \"This example shows how input prompts can be configured at runtime. We have one action that can be triggered by three different input devices. Using the options, you can change what appears in the input prompt and how controller icons will be rendered.\"\nhorizontal_alignment = 1\nautowrap_mode = 2\n\n[node name=\"Spacer\" type=\"Control\" parent=\"CanvasLayer/MarginContainer/VBoxContainer/HBoxContainer\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\n\n[node name=\"CenterContainer\" type=\"CenterContainer\" parent=\"CanvasLayer\"]\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\n\n[node name=\"HBoxContainer\" type=\"HBoxContainer\" parent=\"CanvasLayer/CenterContainer\"]\nlayout_mode = 2\ntheme_override_constants/separation = 40\n\n[node name=\"Label\" type=\"RichTextLabel\" parent=\"CanvasLayer/CenterContainer/HBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntheme = ExtResource(\"7_li4sj\")\nscript = ExtResource(\"8_i8f53\")\ninstructions_text = \"%s to do the magic action!\"\nactions = Array[Resource(\"res://addons/guide/guide_action.gd\")]([ExtResource(\"3_gessh\")])\n\n[node name=\"VBoxContainer\" type=\"VBoxContainer\" parent=\"CanvasLayer/CenterContainer/HBoxContainer\"]\nlayout_mode = 2\n\n[node name=\"DeviceSelection\" type=\"OptionButton\" parent=\"CanvasLayer/CenterContainer/HBoxContainer/VBoxContainer\"]\nlayout_mode = 2\ntheme = ExtResource(\"7_li4sj\")\nitem_count = 2\nselected = 0\npopup/item_0/text = \"Show input for all devices\"\npopup/item_0/id = 0\npopup/item_1/text = \"Show input for currently active device\"\npopup/item_1/id = 1\n\n[node name=\"ControllerTypeOverride\" type=\"OptionButton\" parent=\"CanvasLayer/CenterContainer/HBoxContainer/VBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntheme = ExtResource(\"7_li4sj\")\nitem_count = 5\nselected = 0\npopup/item_0/text = \"Use detected controller type\"\npopup/item_0/id = 0\npopup/item_1/text = \"Force Microsoft Controller\"\npopup/item_1/id = 1\npopup/item_2/text = \"Force Nintendo Controller\"\npopup/item_2/id = 2\npopup/item_3/text = \"Force Sony Controller\"\npopup/item_3/id = 3\npopup/item_4/text = \"Force Steam Deck Controller\"\npopup/item_4/id = 4\n\n[connection signal=\"item_selected\" from=\"CanvasLayer/CenterContainer/HBoxContainer/VBoxContainer/DeviceSelection\" to=\".\" method=\"_on_device_selection_item_selected\"]\n[connection signal=\"item_selected\" from=\"CanvasLayer/CenterContainer/HBoxContainer/VBoxContainer/ControllerTypeOverride\" to=\".\" method=\"_on_controller_type_override_item_selected\"]\n"
  },
  {
    "path": "guide_examples/input_prompts/mapping_contexts/controller_activated.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://dbjyxldjlm3yw\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_ybrft\"]\n\n[resource]\nscript = ExtResource(\"1_ybrft\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/input_prompts/mapping_contexts/fire.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://bxa8jsi2dbtw4\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_sun3f\"]\n\n[resource]\nscript = ExtResource(\"1_sun3f\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/input_prompts/mapping_contexts/input_prompts.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMappingContext\" load_steps=32 format=3 uid=\"uid://b5s7df53wnarw\"]\n\n[ext_resource type=\"Resource\" uid=\"uid://bxa8jsi2dbtw4\" path=\"res://guide_examples/input_prompts/mapping_contexts/fire.tres\" id=\"1_0nxo7\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_mouse_button.gd\" id=\"2_qkbtn\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_input_mapping.gd\" id=\"3_cnf0l\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_pressed.gd\" id=\"4_qofxp\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_key.gd\" id=\"5_pkyus\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_joy_button.gd\" id=\"6_8hvxk\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action_mapping.gd\" id=\"7_it2ir\"]\n[ext_resource type=\"Resource\" uid=\"uid://dbjyxldjlm3yw\" path=\"res://guide_examples/input_prompts/mapping_contexts/controller_activated.tres\" id=\"8_x5e5l\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_any.gd\" id=\"9_cf4y3\"]\n[ext_resource type=\"Resource\" uid=\"uid://dbf0wsbnpx3u8\" path=\"res://guide_examples/input_prompts/mapping_contexts/keyboard_activated.tres\" id=\"10_oy5gs\"]\n[ext_resource type=\"Resource\" uid=\"uid://bpnyp22tg2ccv\" path=\"res://guide_examples/input_prompts/mapping_contexts/mouse_activated.tres\" id=\"11_22tqq\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_mapping_context.gd\" id=\"12_1kflp\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_ivm6r\"]\nscript = ExtResource(\"2_qkbtn\")\nbutton = 2\n\n[sub_resource type=\"Resource\" id=\"Resource_yrlwy\"]\nscript = ExtResource(\"3_cnf0l\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_ivm6r\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_50gdp\"]\nscript = ExtResource(\"5_pkyus\")\nkey = 4194326\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_16huh\"]\nscript = ExtResource(\"3_cnf0l\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_50gdp\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_f1n6u\"]\nscript = ExtResource(\"6_8hvxk\")\nbutton = 0\njoy_index = -1\n\n[sub_resource type=\"Resource\" id=\"Resource_nf6e3\"]\nscript = ExtResource(\"3_cnf0l\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_f1n6u\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_84idq\"]\nscript = ExtResource(\"7_it2ir\")\naction = ExtResource(\"1_0nxo7\")\ninput_mappings = Array[ExtResource(\"3_cnf0l\")]([SubResource(\"Resource_yrlwy\"), SubResource(\"Resource_16huh\"), SubResource(\"Resource_nf6e3\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_w1c6h\"]\nscript = ExtResource(\"9_cf4y3\")\nmouse_buttons = false\nmouse_movement = false\nminimum_mouse_movement_distance = 1.0\njoy_buttons = true\njoy_axes = false\nminimum_joy_axis_actuation_strength = 0.2\nkeyboard = false\ntouch = false\nmouse = false\njoy = true\n\n[sub_resource type=\"Resource\" id=\"Resource_0gbkm\"]\nscript = ExtResource(\"4_qofxp\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_1h7df\"]\nscript = ExtResource(\"3_cnf0l\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_w1c6h\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_0gbkm\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_w1evc\"]\nscript = ExtResource(\"7_it2ir\")\naction = ExtResource(\"8_x5e5l\")\ninput_mappings = Array[ExtResource(\"3_cnf0l\")]([SubResource(\"Resource_1h7df\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_hq8sm\"]\nscript = ExtResource(\"9_cf4y3\")\nmouse_buttons = false\nmouse_movement = false\nminimum_mouse_movement_distance = 1.0\njoy_buttons = false\njoy_axes = false\nminimum_joy_axis_actuation_strength = 0.2\nkeyboard = true\ntouch = false\nmouse = false\njoy = false\n\n[sub_resource type=\"Resource\" id=\"Resource_4rxxw\"]\nscript = ExtResource(\"4_qofxp\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_ojvvu\"]\nscript = ExtResource(\"3_cnf0l\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_hq8sm\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_4rxxw\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_tq6sk\"]\nscript = ExtResource(\"7_it2ir\")\naction = ExtResource(\"10_oy5gs\")\ninput_mappings = Array[ExtResource(\"3_cnf0l\")]([SubResource(\"Resource_ojvvu\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_vjoa0\"]\nscript = ExtResource(\"9_cf4y3\")\nmouse_buttons = true\nmouse_movement = false\nminimum_mouse_movement_distance = 1.0\njoy_buttons = false\njoy_axes = false\nminimum_joy_axis_actuation_strength = 0.2\nkeyboard = false\ntouch = false\nmouse = true\njoy = false\n\n[sub_resource type=\"Resource\" id=\"Resource_1sn2r\"]\nscript = ExtResource(\"4_qofxp\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_0ofgj\"]\nscript = ExtResource(\"3_cnf0l\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_vjoa0\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_1sn2r\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_axasm\"]\nscript = ExtResource(\"7_it2ir\")\naction = ExtResource(\"11_22tqq\")\ninput_mappings = Array[ExtResource(\"3_cnf0l\")]([SubResource(\"Resource_0ofgj\")])\n\n[resource]\nscript = ExtResource(\"12_1kflp\")\ndisplay_name = \"\"\nmappings = Array[ExtResource(\"7_it2ir\")]([SubResource(\"Resource_84idq\"), SubResource(\"Resource_w1evc\"), SubResource(\"Resource_tq6sk\"), SubResource(\"Resource_axasm\")])\n"
  },
  {
    "path": "guide_examples/input_prompts/mapping_contexts/keyboard_activated.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://dbf0wsbnpx3u8\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_cs5ya\"]\n\n[resource]\nscript = ExtResource(\"1_cs5ya\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/input_prompts/mapping_contexts/mouse_activated.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://bpnyp22tg2ccv\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_010c7\"]\n\n[resource]\nscript = ExtResource(\"1_010c7\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/input_scheme_switching/input_scheme_switching.gd",
    "content": "## This example shows how to switch the input scheme on the fly. \nextends Node\n\n@export var joystick_scheme:GUIDEMappingContext\n@export var keyboard_scheme:GUIDEMappingContext\n@export var switch_to_keyboard:GUIDEAction\n@export var switch_to_joystick:GUIDEAction\n\nfunc _ready() -> void:\n\t# When we get a command to switch the input scheme, we\n\t# switch.\n\tswitch_to_keyboard.triggered.connect(_switch_input_scheme.bind(keyboard_scheme))\n\tswitch_to_joystick.triggered.connect(_switch_input_scheme.bind(joystick_scheme))\n\t\n\t# And switch now to enable keyboard\n\t_switch_input_scheme(keyboard_scheme)\n\n\nfunc _switch_input_scheme(context:GUIDEMappingContext) -> void:\n\tGUIDE.enable_mapping_context(context, true)\n\t\n"
  },
  {
    "path": "guide_examples/input_scheme_switching/input_scheme_switching.gd.uid",
    "content": "uid://fhw2j8umfqty\n"
  },
  {
    "path": "guide_examples/input_scheme_switching/input_scheme_switching.tscn",
    "content": "[gd_scene load_steps=14 format=3 uid=\"uid://dvbxt8jyo8okp\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/input_scheme_switching/input_scheme_switching.gd\" id=\"1_7l2n1\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://byjxtsekdl8t2\" path=\"res://guide_examples/shared/godot_logo.svg\" id=\"1_t4jeg\"]\n[ext_resource type=\"Resource\" uid=\"uid://x33fk5wo7l2r\" path=\"res://guide_examples/input_scheme_switching/mapping_contexts/joystick_scheme.tres\" id=\"2_fx1v6\"]\n[ext_resource type=\"Resource\" uid=\"uid://2hl7iqpondhi\" path=\"res://guide_examples/input_scheme_switching/mapping_contexts/keyboard_scheme.tres\" id=\"3_pvyn2\"]\n[ext_resource type=\"Resource\" uid=\"uid://cxn2ibe1mn3sb\" path=\"res://guide_examples/input_scheme_switching/mapping_contexts/actions/move.tres\" id=\"4_3bnea\"]\n[ext_resource type=\"Resource\" uid=\"uid://b11rcmd3hse58\" path=\"res://guide_examples/input_scheme_switching/mapping_contexts/actions/switch_to_keyboard.tres\" id=\"4_tts5j\"]\n[ext_resource type=\"Resource\" uid=\"uid://c7htf8h44vbwi\" path=\"res://guide_examples/input_scheme_switching/mapping_contexts/actions/shoot.tres\" id=\"5_4yg1b\"]\n[ext_resource type=\"Resource\" uid=\"uid://vctiwgvnl0ba\" path=\"res://guide_examples/input_scheme_switching/mapping_contexts/actions/switch_to_joystick.tres\" id=\"6_b05vw\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/input_scheme_switching/player.gd\" id=\"7_2r4ev\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://dkr80d2pi0d41\" path=\"res://addons/guide/debugger/guide_debugger.tscn\" id=\"8_nv6u5\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://c36cnvgv2ur60\" path=\"res://guide_examples/shared/fireball/fireball.tscn\" id=\"8_t3npb\"]\n[ext_resource type=\"Theme\" uid=\"uid://dot0gi1yoqmrl\" path=\"res://guide_examples/shared/ui_theme.tres\" id=\"10_vp4t3\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/shared/instructions_label.gd\" id=\"11_qdni4\"]\n\n[node name=\"InputSchemeSwitching\" type=\"Node2D\"]\nscript = ExtResource(\"1_7l2n1\")\njoystick_scheme = ExtResource(\"2_fx1v6\")\nkeyboard_scheme = ExtResource(\"3_pvyn2\")\nswitch_to_keyboard = ExtResource(\"4_tts5j\")\nswitch_to_joystick = ExtResource(\"6_b05vw\")\n\n[node name=\"Player\" type=\"Node2D\" parent=\".\"]\nposition = Vector2(929, 695)\nscript = ExtResource(\"7_2r4ev\")\nspeed = 300.0\nmove_action = ExtResource(\"4_3bnea\")\nshoot_action = ExtResource(\"5_4yg1b\")\nfireball_scene = ExtResource(\"8_t3npb\")\n\n[node name=\"GodotLogo\" type=\"Sprite2D\" parent=\"Player\"]\ntexture = ExtResource(\"1_t4jeg\")\n\n[node name=\"UI\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"InstructionsLabel\" type=\"RichTextLabel\" parent=\"UI\"]\nunique_name_in_owner = true\ncustom_minimum_size = Vector2(500, 0)\nanchors_preset = 1\nanchor_left = 1.0\nanchor_right = 1.0\noffset_left = -552.0\noffset_top = 85.0\noffset_right = -52.0\noffset_bottom = 125.0\ngrow_horizontal = 0\ntheme = ExtResource(\"10_vp4t3\")\nbbcode_enabled = true\ntext = \"Lorem Ipsum Dolor\"\nfit_content = true\nscript = ExtResource(\"11_qdni4\")\ninstructions_text = \"%s to move.\n%s to shoot a fireball.\"\nactions = Array[Resource(\"res://addons/guide/guide_action.gd\")]([ExtResource(\"4_3bnea\"), ExtResource(\"5_4yg1b\")])\n\n[node name=\"Debugger\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"GuideDebugger\" parent=\"Debugger\" instance=ExtResource(\"8_nv6u5\")]\ntheme = ExtResource(\"10_vp4t3\")\n"
  },
  {
    "path": "guide_examples/input_scheme_switching/mapping_contexts/actions/move.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://cxn2ibe1mn3sb\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_anxy1\"]\n\n[resource]\nscript = ExtResource(\"1_anxy1\")\nname = &\"\"\naction_value_type = 2\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/input_scheme_switching/mapping_contexts/actions/shoot.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://c7htf8h44vbwi\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_bkoj8\"]\n\n[resource]\nscript = ExtResource(\"1_bkoj8\")\nname = &\"\"\naction_value_type = 0\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/input_scheme_switching/mapping_contexts/actions/switch_to_joystick.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://vctiwgvnl0ba\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_8q327\"]\n\n[resource]\nscript = ExtResource(\"1_8q327\")\nname = &\"\"\naction_value_type = 0\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/input_scheme_switching/mapping_contexts/actions/switch_to_keyboard.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://b11rcmd3hse58\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_4oh0y\"]\n\n[resource]\nscript = ExtResource(\"1_4oh0y\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/input_scheme_switching/mapping_contexts/joystick_scheme.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMappingContext\" load_steps=23 format=3 uid=\"uid://x33fk5wo7l2r\"]\n\n[ext_resource type=\"Resource\" uid=\"uid://cxn2ibe1mn3sb\" path=\"res://guide_examples/input_scheme_switching/mapping_contexts/actions/move.tres\" id=\"1_hupae\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_joy_axis_2d.gd\" id=\"2_t6p7a\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_input_mapping.gd\" id=\"3_6jy3p\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_deadzone.gd\" id=\"3_c1g58\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action_mapping.gd\" id=\"4_ssubt\"]\n[ext_resource type=\"Resource\" uid=\"uid://c7htf8h44vbwi\" path=\"res://guide_examples/input_scheme_switching/mapping_contexts/actions/shoot.tres\" id=\"5_pd4bb\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_joy_button.gd\" id=\"6_eieuy\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_pressed.gd\" id=\"7_0nn3e\"]\n[ext_resource type=\"Resource\" uid=\"uid://b11rcmd3hse58\" path=\"res://guide_examples/input_scheme_switching/mapping_contexts/actions/switch_to_keyboard.tres\" id=\"8_7nlt6\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_any.gd\" id=\"8_hdd7g\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_mapping_context.gd\" id=\"9_1vdw8\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_7qyuq\"]\nscript = ExtResource(\"2_t6p7a\")\nx = 0\ny = 1\njoy_index = -1\n\n[sub_resource type=\"Resource\" id=\"Resource_8pjab\"]\nscript = ExtResource(\"3_c1g58\")\nlower_threshold = 0.2\nupper_threshold = 1.0\n\n[sub_resource type=\"Resource\" id=\"Resource_wxd67\"]\nscript = ExtResource(\"3_6jy3p\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_7qyuq\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_8pjab\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_lomew\"]\nscript = ExtResource(\"4_ssubt\")\naction = ExtResource(\"1_hupae\")\ninput_mappings = Array[ExtResource(\"3_6jy3p\")]([SubResource(\"Resource_wxd67\")])\nmetadata/_guide_input_mappings_collapsed = false\n\n[sub_resource type=\"Resource\" id=\"Resource_guotu\"]\nscript = ExtResource(\"6_eieuy\")\nbutton = 0\njoy_index = -1\n\n[sub_resource type=\"Resource\" id=\"Resource_ws3e0\"]\nscript = ExtResource(\"7_0nn3e\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_b21ub\"]\nscript = ExtResource(\"3_6jy3p\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_guotu\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_ws3e0\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_tmt5r\"]\nscript = ExtResource(\"4_ssubt\")\naction = ExtResource(\"5_pd4bb\")\ninput_mappings = Array[ExtResource(\"3_6jy3p\")]([SubResource(\"Resource_b21ub\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_41j1i\"]\nscript = ExtResource(\"8_hdd7g\")\nmouse_buttons = false\nmouse_movement = false\nminimum_mouse_movement_distance = 1.0\njoy_buttons = false\njoy_axes = false\nminimum_joy_axis_actuation_strength = 0.2\nkeyboard = true\ntouch = false\nmouse = false\njoy = false\n\n[sub_resource type=\"Resource\" id=\"Resource_ncpby\"]\nscript = ExtResource(\"3_6jy3p\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_41j1i\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_28cwt\"]\nscript = ExtResource(\"4_ssubt\")\naction = ExtResource(\"8_7nlt6\")\ninput_mappings = Array[ExtResource(\"3_6jy3p\")]([SubResource(\"Resource_ncpby\")])\n\n[resource]\nscript = ExtResource(\"9_1vdw8\")\ndisplay_name = \"\"\nmappings = Array[ExtResource(\"4_ssubt\")]([SubResource(\"Resource_lomew\"), SubResource(\"Resource_tmt5r\"), SubResource(\"Resource_28cwt\")])\n"
  },
  {
    "path": "guide_examples/input_scheme_switching/mapping_contexts/keyboard_scheme.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMappingContext\" load_steps=32 format=3 uid=\"uid://2hl7iqpondhi\"]\n\n[ext_resource type=\"Resource\" uid=\"uid://cxn2ibe1mn3sb\" path=\"res://guide_examples/input_scheme_switching/mapping_contexts/actions/move.tres\" id=\"1_wyjhr\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_key.gd\" id=\"2_10ro5\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_input_swizzle.gd\" id=\"3_uaw8o\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_negate.gd\" id=\"4_hv31f\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_input_mapping.gd\" id=\"5_v5abd\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action_mapping.gd\" id=\"6_5qe2r\"]\n[ext_resource type=\"Resource\" uid=\"uid://c7htf8h44vbwi\" path=\"res://guide_examples/input_scheme_switching/mapping_contexts/actions/shoot.tres\" id=\"7_diyqh\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_pressed.gd\" id=\"8_enquy\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_any.gd\" id=\"9_j1ko4\"]\n[ext_resource type=\"Resource\" uid=\"uid://vctiwgvnl0ba\" path=\"res://guide_examples/input_scheme_switching/mapping_contexts/actions/switch_to_joystick.tres\" id=\"9_p8ck7\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_mapping_context.gd\" id=\"11_rkw8m\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_ckatg\"]\nscript = ExtResource(\"2_10ro5\")\nkey = 87\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_8rr5k\"]\nscript = ExtResource(\"3_uaw8o\")\norder = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_4c38v\"]\nscript = ExtResource(\"4_hv31f\")\nx = true\ny = true\nz = true\n\n[sub_resource type=\"Resource\" id=\"Resource_xot0n\"]\nscript = ExtResource(\"5_v5abd\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_ckatg\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_8rr5k\"), SubResource(\"Resource_4c38v\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_v2una\"]\nscript = ExtResource(\"2_10ro5\")\nkey = 65\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_tu604\"]\nscript = ExtResource(\"4_hv31f\")\nx = true\ny = true\nz = true\n\n[sub_resource type=\"Resource\" id=\"Resource_vh7xg\"]\nscript = ExtResource(\"5_v5abd\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_v2una\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_tu604\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_sko3c\"]\nscript = ExtResource(\"2_10ro5\")\nkey = 83\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_x4ewt\"]\nscript = ExtResource(\"3_uaw8o\")\norder = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_1bv7n\"]\nscript = ExtResource(\"5_v5abd\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_sko3c\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_x4ewt\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_o6q2e\"]\nscript = ExtResource(\"2_10ro5\")\nkey = 68\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_ggp85\"]\nscript = ExtResource(\"5_v5abd\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_o6q2e\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_lomew\"]\nscript = ExtResource(\"6_5qe2r\")\naction = ExtResource(\"1_wyjhr\")\ninput_mappings = Array[ExtResource(\"5_v5abd\")]([SubResource(\"Resource_xot0n\"), SubResource(\"Resource_vh7xg\"), SubResource(\"Resource_1bv7n\"), SubResource(\"Resource_ggp85\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_wxbv3\"]\nscript = ExtResource(\"2_10ro5\")\nkey = 32\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_a515r\"]\nscript = ExtResource(\"8_enquy\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_u0geb\"]\nscript = ExtResource(\"5_v5abd\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_wxbv3\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_a515r\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_f8tqv\"]\nscript = ExtResource(\"6_5qe2r\")\naction = ExtResource(\"7_diyqh\")\ninput_mappings = Array[ExtResource(\"5_v5abd\")]([SubResource(\"Resource_u0geb\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_1wnrb\"]\nscript = ExtResource(\"9_j1ko4\")\nmouse_buttons = false\nmouse_movement = false\nminimum_mouse_movement_distance = 1.0\njoy_buttons = true\njoy_axes = false\nminimum_joy_axis_actuation_strength = 0.2\nkeyboard = false\ntouch = false\nmouse = false\njoy = true\n\n[sub_resource type=\"Resource\" id=\"Resource_i18os\"]\nscript = ExtResource(\"5_v5abd\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_1wnrb\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_im7a2\"]\nscript = ExtResource(\"6_5qe2r\")\naction = ExtResource(\"9_p8ck7\")\ninput_mappings = Array[ExtResource(\"5_v5abd\")]([SubResource(\"Resource_i18os\")])\n\n[resource]\nscript = ExtResource(\"11_rkw8m\")\ndisplay_name = \"\"\nmappings = Array[ExtResource(\"6_5qe2r\")]([SubResource(\"Resource_lomew\"), SubResource(\"Resource_f8tqv\"), SubResource(\"Resource_im7a2\")])\n"
  },
  {
    "path": "guide_examples/input_scheme_switching/player.gd",
    "content": "## Our player. The player has no knowledge about input schemes, it just\n## reacts to actions triggering.\nextends Node2D\n\n@export var speed:float = 200\n\n@export var move_action:GUIDEAction\n@export var shoot_action:GUIDEAction\n@export var fireball_scene:PackedScene\n\n\nfunc _ready() -> void:\n\tshoot_action.triggered.connect(_shoot_fireball)\n\n\nfunc _process(delta:float) -> void:\n\tposition += move_action.value_axis_2d.normalized() * speed * delta\n\n\nfunc _shoot_fireball() -> void:\n\tvar fireball:Node = fireball_scene.instantiate()\n\tfireball.direction = Vector2.UP\n\tget_parent().add_child(fireball)\n\t\n\tfireball.global_transform = global_transform\n\t\n\t\n"
  },
  {
    "path": "guide_examples/input_scheme_switching/player.gd.uid",
    "content": "uid://csjgtlek7infj\n"
  },
  {
    "path": "guide_examples/mouse_position_2d/background.gd",
    "content": "## This just keeps the sprite endlessly scrolling. It's not related to input.\nextends Sprite2D\n\n\nfunc _process(_delta:float) -> void:\n\t# get rect of visible screen in world coordinates\n\tvar rect := get_viewport().canvas_transform.affine_inverse() * get_viewport_rect()\n\t# fit the bg into the viewport\n\tglobal_position = rect.position\n\tglobal_scale =  rect.size / texture.get_size()\n\t\n\t# update scaling so the texture scales according to zoom level\n\tmaterial.set_shader_parameter(\"scale\", global_scale)\n\tvar shader_offset := rect.position / texture.get_size()\n\t# and offset so we pick a texture offset relative to the movement of the camera\n\tmaterial.set_shader_parameter(\"offset\", shader_offset)\n"
  },
  {
    "path": "guide_examples/mouse_position_2d/background.gd.uid",
    "content": "uid://by6w26jgvaitm\n"
  },
  {
    "path": "guide_examples/mouse_position_2d/background.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://slnmn5k0drdb\"\npath=\"res://.godot/imported/background.svg-2c00776905f8df1964b7da3b2242aa3e.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://guide_examples/mouse_position_2d/background.svg\"\ndest_files=[\"res://.godot/imported/background.svg-2c00776905f8df1964b7da3b2242aa3e.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=false\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "guide_examples/mouse_position_2d/camera_2d.gd",
    "content": "## Camera control. We listen to GUIDE's actions to move and zoom the camera. Note how we can\n## mix event-based and polling based input handling, depending on what works better for the \n## use case.\nextends Camera2D\n\n\n@export var camera_movement:GUIDEAction\n@export var camera_zoom:GUIDEAction\n@export var speed:float = 300\n\n\nfunc _ready() -> void:\n\tcamera_zoom.triggered.connect(_zoom_camera)\n\nfunc _process(delta:float) -> void:\n\tposition += camera_movement.value_axis_2d * speed * delta\n\t\nfunc _zoom_camera() -> void:\n\tzoom = clamp( zoom + Vector2.ONE * camera_zoom.value_axis_1d, Vector2(0.1, 0.1), Vector2(3, 3))\n"
  },
  {
    "path": "guide_examples/mouse_position_2d/camera_2d.gd.uid",
    "content": "uid://cndto72qu3boe\n"
  },
  {
    "path": "guide_examples/mouse_position_2d/godot_head.gd",
    "content": "extends Node2D\n\n@export var lifetime_seconds:float = 5.0\nvar _remaining_time_seconds:float = 0\n\nfunc _ready() -> void:\n\t_remaining_time_seconds = lifetime_seconds\n\nfunc _process(delta:float) -> void:\n\t_remaining_time_seconds -= delta\n\tif _remaining_time_seconds <= 0:\n\t\tqueue_free()\n\t\treturn\n\t\n\tmodulate.a = _remaining_time_seconds / lifetime_seconds\n"
  },
  {
    "path": "guide_examples/mouse_position_2d/godot_head.gd.uid",
    "content": "uid://c5wpkmya4n248\n"
  },
  {
    "path": "guide_examples/mouse_position_2d/godot_head.tscn",
    "content": "[gd_scene load_steps=3 format=3 uid=\"uid://cj8m2n32yjxka\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/mouse_position_2d/godot_head.gd\" id=\"1_7od3t\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://byjxtsekdl8t2\" path=\"res://guide_examples/shared/godot_logo.svg\" id=\"1_1168h\"]\n\n[node name=\"GodotHead\" type=\"Node2D\"]\nscript = ExtResource(\"1_7od3t\")\n\n[node name=\"Sprite2D\" type=\"Sprite2D\" parent=\".\"]\ntexture = ExtResource(\"1_1168h\")\n"
  },
  {
    "path": "guide_examples/mouse_position_2d/mapping_contexts/actions/camera_movement.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://ehdejslyo58y\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_clx3u\"]\n\n[resource]\nscript = ExtResource(\"1_clx3u\")\nname = &\"\"\naction_value_type = 2\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/mouse_position_2d/mapping_contexts/actions/camera_zoom.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://6dm5j1sdhdp2\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_u52q5\"]\n\n[resource]\nscript = ExtResource(\"1_u52q5\")\nname = &\"\"\naction_value_type = 1\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/mouse_position_2d/mapping_contexts/actions/cursor.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://y7q516rtjlt8\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_casem\"]\n\n[resource]\nscript = ExtResource(\"1_casem\")\nname = &\"\"\naction_value_type = 2\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/mouse_position_2d/mapping_contexts/actions/spawn.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://cap7r63x8tait\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_y7wt7\"]\n\n[resource]\nscript = ExtResource(\"1_y7wt7\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/mouse_position_2d/mapping_contexts/modifiers/zoom_sensitivity.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEModifierScale\" load_steps=2 format=3 uid=\"uid://d0brjke26hsk8\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_scale.gd\" id=\"1_vqbea\"]\n\n[resource]\nscript = ExtResource(\"1_vqbea\")\nscale = Vector3(0.1, 1, 1)\napply_delta_time = false\n"
  },
  {
    "path": "guide_examples/mouse_position_2d/mapping_contexts/mouse_position.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMappingContext\" load_steps=43 format=3 uid=\"uid://cfbk5croqnocs\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_mapping_context.gd\" id=\"1_ru5nv\"]\n[ext_resource type=\"Resource\" uid=\"uid://y7q516rtjlt8\" path=\"res://guide_examples/mouse_position_2d/mapping_contexts/actions/cursor.tres\" id=\"1_ybilq\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_mouse_position.gd\" id=\"2_xu301\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_input_mapping.gd\" id=\"3_vy8se\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action_mapping.gd\" id=\"4_pisoh\"]\n[ext_resource type=\"Resource\" uid=\"uid://ehdejslyo58y\" path=\"res://guide_examples/mouse_position_2d/mapping_contexts/actions/camera_movement.tres\" id=\"6_qfh27\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_key.gd\" id=\"6_u43ni\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_negate.gd\" id=\"7_668rf\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_input_swizzle.gd\" id=\"8_o7tqa\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_mouse_button.gd\" id=\"10_g5tce\"]\n[ext_resource type=\"Resource\" uid=\"uid://cap7r63x8tait\" path=\"res://guide_examples/mouse_position_2d/mapping_contexts/actions/spawn.tres\" id=\"10_ohwve\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_pressed.gd\" id=\"11_5ifuu\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_canvas_coordinates.gd\" id=\"11_j8wbm\"]\n[ext_resource type=\"Resource\" uid=\"uid://6dm5j1sdhdp2\" path=\"res://guide_examples/mouse_position_2d/mapping_contexts/actions/camera_zoom.tres\" id=\"13_htiyk\"]\n[ext_resource type=\"Resource\" uid=\"uid://d0brjke26hsk8\" path=\"res://guide_examples/mouse_position_2d/mapping_contexts/modifiers/zoom_sensitivity.tres\" id=\"14_0gubt\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_420bm\"]\nscript = ExtResource(\"2_xu301\")\n\n[sub_resource type=\"Resource\" id=\"Resource_hqwk3\"]\nscript = ExtResource(\"11_j8wbm\")\nrelative_input = false\n\n[sub_resource type=\"Resource\" id=\"Resource_wsyf2\"]\nscript = ExtResource(\"3_vy8se\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_420bm\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_hqwk3\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_4f0pb\"]\nscript = ExtResource(\"4_pisoh\")\naction = ExtResource(\"1_ybilq\")\ninput_mappings = Array[ExtResource(\"3_vy8se\")]([SubResource(\"Resource_wsyf2\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_jlr8l\"]\nscript = ExtResource(\"6_u43ni\")\nkey = 87\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_o78ws\"]\nscript = ExtResource(\"7_668rf\")\nx = true\ny = true\nz = true\n\n[sub_resource type=\"Resource\" id=\"Resource_8gssa\"]\nscript = ExtResource(\"8_o7tqa\")\norder = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_qmv1n\"]\nscript = ExtResource(\"3_vy8se\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_jlr8l\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_o78ws\"), SubResource(\"Resource_8gssa\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_7q6a3\"]\nscript = ExtResource(\"6_u43ni\")\nkey = 83\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_l8svn\"]\nscript = ExtResource(\"8_o7tqa\")\norder = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_j8u0l\"]\nscript = ExtResource(\"3_vy8se\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_7q6a3\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_l8svn\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_asguk\"]\nscript = ExtResource(\"6_u43ni\")\nkey = 65\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_ly8fj\"]\nscript = ExtResource(\"7_668rf\")\nx = true\ny = true\nz = true\n\n[sub_resource type=\"Resource\" id=\"Resource_6m3qh\"]\nscript = ExtResource(\"3_vy8se\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_asguk\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_ly8fj\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_vbr6o\"]\nscript = ExtResource(\"6_u43ni\")\nkey = 68\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_xiclq\"]\nscript = ExtResource(\"3_vy8se\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_vbr6o\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_hvdyr\"]\nscript = ExtResource(\"4_pisoh\")\naction = ExtResource(\"6_qfh27\")\ninput_mappings = Array[ExtResource(\"3_vy8se\")]([SubResource(\"Resource_qmv1n\"), SubResource(\"Resource_j8u0l\"), SubResource(\"Resource_6m3qh\"), SubResource(\"Resource_xiclq\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_3v4no\"]\nscript = ExtResource(\"10_g5tce\")\nbutton = 4\n\n[sub_resource type=\"Resource\" id=\"Resource_l152p\"]\nscript = ExtResource(\"3_vy8se\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_3v4no\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([ExtResource(\"14_0gubt\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_wlhft\"]\nscript = ExtResource(\"10_g5tce\")\nbutton = 5\n\n[sub_resource type=\"Resource\" id=\"Resource_b581m\"]\nscript = ExtResource(\"7_668rf\")\nx = true\ny = true\nz = true\n\n[sub_resource type=\"Resource\" id=\"Resource_xrajm\"]\nscript = ExtResource(\"3_vy8se\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_wlhft\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_b581m\"), ExtResource(\"14_0gubt\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_otc05\"]\nscript = ExtResource(\"4_pisoh\")\naction = ExtResource(\"13_htiyk\")\ninput_mappings = Array[ExtResource(\"3_vy8se\")]([SubResource(\"Resource_l152p\"), SubResource(\"Resource_xrajm\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_w5sbr\"]\nscript = ExtResource(\"10_g5tce\")\nbutton = 1\n\n[sub_resource type=\"Resource\" id=\"Resource_omxoi\"]\nscript = ExtResource(\"11_5ifuu\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_8n6aa\"]\nscript = ExtResource(\"3_vy8se\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_w5sbr\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_omxoi\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_iivaa\"]\nscript = ExtResource(\"4_pisoh\")\naction = ExtResource(\"10_ohwve\")\ninput_mappings = Array[ExtResource(\"3_vy8se\")]([SubResource(\"Resource_8n6aa\")])\n\n[resource]\nscript = ExtResource(\"1_ru5nv\")\ndisplay_name = \"\"\nmappings = Array[ExtResource(\"4_pisoh\")]([SubResource(\"Resource_4f0pb\"), SubResource(\"Resource_hvdyr\"), SubResource(\"Resource_otc05\"), SubResource(\"Resource_iivaa\")])\n"
  },
  {
    "path": "guide_examples/mouse_position_2d/mouse_position.gd",
    "content": "## This example shows how to get access to the mouse cursor without being\n## specific about where the input comes from. \nextends Node2D\n\n\n@export var mapping_context:GUIDEMappingContext\n@export var spawn:GUIDEAction\n@export var cursor:GUIDEAction\n\n@export var godot_head_scene:PackedScene\n\nfunc _ready() -> void:\n\tGUIDE.enable_mapping_context(mapping_context)\n\tspawn.triggered.connect(_spawn_godot_head)\n\n\nfunc _spawn_godot_head() -> void:\n\t# Gets the mouse cursor from G.U.I.D.E. Note how the Canvas Coordinates\n\t# modifier automatically gives us mouse coordinates in canvas space\n\t# which means we don't need to take into acount the camera panning and \n\t# zoom level and can just use the coordinates we get to directly place\n\t# a Godot head at the cursor position. \n\tvar head:Node = godot_head_scene.instantiate()\n\tadd_child(head)\n\t\n\thead.global_position = cursor.value_axis_2d\n"
  },
  {
    "path": "guide_examples/mouse_position_2d/mouse_position.gd.uid",
    "content": "uid://cvoeomxm2dwyr\n"
  },
  {
    "path": "guide_examples/mouse_position_2d/mouse_position.tscn",
    "content": "[gd_scene load_steps=16 format=3 uid=\"uid://c4de28wapdqtp\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/mouse_position_2d/mouse_position.gd\" id=\"1_rkyn8\"]\n[ext_resource type=\"Resource\" uid=\"uid://cfbk5croqnocs\" path=\"res://guide_examples/mouse_position_2d/mapping_contexts/mouse_position.tres\" id=\"2_f4xly\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://dkr80d2pi0d41\" path=\"res://addons/guide/debugger/guide_debugger.tscn\" id=\"2_yylue\"]\n[ext_resource type=\"Resource\" uid=\"uid://y7q516rtjlt8\" path=\"res://guide_examples/mouse_position_2d/mapping_contexts/actions/cursor.tres\" id=\"3_e2cui\"]\n[ext_resource type=\"Resource\" uid=\"uid://cap7r63x8tait\" path=\"res://guide_examples/mouse_position_2d/mapping_contexts/actions/spawn.tres\" id=\"3_e16oi\"]\n[ext_resource type=\"Theme\" uid=\"uid://dot0gi1yoqmrl\" path=\"res://guide_examples/shared/ui_theme.tres\" id=\"3_xcjwc\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/mouse_position_2d/camera_2d.gd\" id=\"3_xpjlw\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://cj8m2n32yjxka\" path=\"res://guide_examples/mouse_position_2d/godot_head.tscn\" id=\"5_6xobh\"]\n[ext_resource type=\"Resource\" uid=\"uid://ehdejslyo58y\" path=\"res://guide_examples/mouse_position_2d/mapping_contexts/actions/camera_movement.tres\" id=\"5_snwnm\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://slnmn5k0drdb\" path=\"res://guide_examples/mouse_position_2d/background.svg\" id=\"6_1tobk\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/mouse_position_2d/background.gd\" id=\"7_4oihe\"]\n[ext_resource type=\"Resource\" uid=\"uid://6dm5j1sdhdp2\" path=\"res://guide_examples/mouse_position_2d/mapping_contexts/actions/camera_zoom.tres\" id=\"8_6tg1h\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/shared/instructions_label.gd\" id=\"9_y8piq\"]\n\n[sub_resource type=\"Shader\" id=\"Shader_v4pj1\"]\ncode = \"shader_type canvas_item;\n\nuniform vec2 scale;\nuniform vec2 offset;\n\nvoid vertex() {\n\tUV =  UV * scale + offset;\n}\n\n\n//void light() {\n\t// Called for every pixel for every light affecting the CanvasItem.\n\t// Uncomment to replace the default light processing function with this one.\n//}\n\"\n\n[sub_resource type=\"ShaderMaterial\" id=\"ShaderMaterial_1sa2x\"]\nshader = SubResource(\"Shader_v4pj1\")\nshader_parameter/scale = Vector2(1, 1)\nshader_parameter/offset = Vector2(0, 0)\n\n[node name=\"MousePosition\" type=\"Node2D\"]\nscript = ExtResource(\"1_rkyn8\")\nmapping_context = ExtResource(\"2_f4xly\")\nspawn = ExtResource(\"3_e16oi\")\ncursor = ExtResource(\"3_e2cui\")\ngodot_head_scene = ExtResource(\"5_6xobh\")\n\n[node name=\"Camera2D\" type=\"Camera2D\" parent=\".\"]\nscript = ExtResource(\"3_xpjlw\")\ncamera_movement = ExtResource(\"5_snwnm\")\ncamera_zoom = ExtResource(\"8_6tg1h\")\n\n[node name=\"BG\" type=\"Sprite2D\" parent=\".\"]\ntexture_repeat = 2\nmaterial = SubResource(\"ShaderMaterial_1sa2x\")\ntexture = ExtResource(\"6_1tobk\")\ncentered = false\nscript = ExtResource(\"7_4oihe\")\n\n[node name=\"UILayer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"RichTextLabel\" type=\"RichTextLabel\" parent=\"UILayer\"]\nanchors_preset = 1\nanchor_left = 1.0\nanchor_right = 1.0\noffset_left = -107.0\noffset_top = 41.0\noffset_right = -67.0\noffset_bottom = 81.0\ngrow_horizontal = 0\ntheme = ExtResource(\"3_xcjwc\")\nscript = ExtResource(\"9_y8piq\")\ninstructions_text = \"%s to move the camera.\n%s to zoom the camera.\n%s to place a Godot head.\"\nactions = Array[Resource(\"res://addons/guide/guide_action.gd\")]([ExtResource(\"5_snwnm\"), ExtResource(\"8_6tg1h\"), ExtResource(\"3_e16oi\")])\n\n[node name=\"DebuggerLayer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"GuideDebugger\" parent=\"DebuggerLayer\" instance=ExtResource(\"2_yylue\")]\ntheme = ExtResource(\"3_xcjwc\")\n"
  },
  {
    "path": "guide_examples/mouse_position_3d/camera_control.gd",
    "content": "## GUIDE makes controlling a camera pretty easy. By using the \n## window-relative and scale modifiers we can translate mouse input \n## directly into a format suitable for rotation. GUIDE also takes\n## care of only sending yaw and pitch input when the camera toggle \n## is pressed, so we don't need to have any complex input code in\n## our camera control script.\nextends Node3D\n\n@export var camera_pitch:GUIDEAction\n@export var camera_yaw:GUIDEAction\n@export var camera_toggle:GUIDEAction\n@export var camera_move:GUIDEAction\n\n@export var movement_speed:float = 1\n@onready var _camera_yaw:Node3D = %CameraYaw\n@onready var _camera_pitch:SpringArm3D = %CameraPitch\n\nfunc _ready() -> void:\n\tcamera_toggle.triggered.connect(_hide_mouse)\n\tcamera_toggle.completed.connect(_show_mouse)\n\tcamera_yaw.triggered.connect(_yaw)\n\tcamera_pitch.triggered.connect(_pitch)\n\t\nfunc _hide_mouse() -> void:\n\tInput.mouse_mode = Input.MOUSE_MODE_CAPTURED\n\t\nfunc _show_mouse() -> void:\n\tInput.mouse_mode = Input.MOUSE_MODE_VISIBLE\n\nfunc _yaw() -> void:\n\t_camera_yaw.rotate_y(camera_yaw.value_axis_1d)\n\nfunc _pitch() -> void:\n\t_camera_pitch.rotate_x(camera_pitch.value_axis_1d)\t\n\t_camera_pitch.rotation_degrees.x = clamp(_camera_pitch.rotation_degrees.x, -75.0, 0.0)\n\n\t\t\nfunc _process(delta:float) -> void:\n\t# we already used the input-swizzle modifier to get forward as -z, backward as z\n\t# left as -x and right as x, so we can use this immediately\n\tposition += basis * camera_move.value_axis_3d * movement_speed * delta\n\t\n"
  },
  {
    "path": "guide_examples/mouse_position_3d/camera_control.gd.uid",
    "content": "uid://c560cjrqx328f\n"
  },
  {
    "path": "guide_examples/mouse_position_3d/mapping_context/actions/camera_move.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://ceti1avx1l1qw\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_ehoni\"]\n\n[resource]\nscript = ExtResource(\"1_ehoni\")\nname = &\"\"\naction_value_type = 3\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/mouse_position_3d/mapping_context/actions/camera_pitch.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://bago2pmgvpepb\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_uluq2\"]\n\n[resource]\nscript = ExtResource(\"1_uluq2\")\nname = &\"\"\naction_value_type = 1\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/mouse_position_3d/mapping_context/actions/camera_toggle.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://cpljlaavuq515\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_gal8q\"]\n\n[resource]\nscript = ExtResource(\"1_gal8q\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/mouse_position_3d/mapping_context/actions/camera_yaw.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://bltmi1lr7umq0\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_273ub\"]\n\n[resource]\nscript = ExtResource(\"1_273ub\")\nname = &\"\"\naction_value_type = 1\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/mouse_position_3d/mapping_context/actions/cursor_3d.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://2b0ncq7ogvv0\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_3to1y\"]\n\n[resource]\nscript = ExtResource(\"1_3to1y\")\nname = &\"\"\naction_value_type = 3\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/mouse_position_3d/mapping_context/actions/select.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://b2jb27062t2mh\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_nysth\"]\n\n[resource]\nscript = ExtResource(\"1_nysth\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/mouse_position_3d/mapping_context/mouse_position_3d.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMappingContext\" load_steps=59 format=3 uid=\"uid://bpatu7vi2kj4l\"]\n\n[ext_resource type=\"Resource\" uid=\"uid://bltmi1lr7umq0\" path=\"res://guide_examples/mouse_position_3d/mapping_context/actions/camera_yaw.tres\" id=\"1_8aom2\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_mapping_context.gd\" id=\"1_rde8c\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_mouse_position.gd\" id=\"2_nomn0\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_mouse_axis_1d.gd\" id=\"2_qlocd\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_window_relative.gd\" id=\"3_p61vs\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_3d_coordinates.gd\" id=\"3_uds3g\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_input_mapping.gd\" id=\"4_6a0qh\"]\n[ext_resource type=\"Resource\" uid=\"uid://cpljlaavuq515\" path=\"res://guide_examples/mouse_position_3d/mapping_context/actions/camera_toggle.tres\" id=\"4_jyoym\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_negate.gd\" id=\"4_mho8x\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action_mapping.gd\" id=\"5_4hkur\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_scale.gd\" id=\"5_b42iu\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_chorded_action.gd\" id=\"5_qvvu8\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_mouse_button.gd\" id=\"7_7w5i6\"]\n[ext_resource type=\"Resource\" uid=\"uid://bago2pmgvpepb\" path=\"res://guide_examples/mouse_position_3d/mapping_context/actions/camera_pitch.tres\" id=\"7_swef3\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_pressed.gd\" id=\"8_5glyu\"]\n[ext_resource type=\"Resource\" uid=\"uid://2b0ncq7ogvv0\" path=\"res://guide_examples/mouse_position_3d/mapping_context/actions/cursor_3d.tres\" id=\"9_rxgkr\"]\n[ext_resource type=\"Resource\" uid=\"uid://ceti1avx1l1qw\" path=\"res://guide_examples/mouse_position_3d/mapping_context/actions/camera_move.tres\" id=\"12_v8r8p\"]\n[ext_resource type=\"Resource\" uid=\"uid://b2jb27062t2mh\" path=\"res://guide_examples/mouse_position_3d/mapping_context/actions/select.tres\" id=\"12_wmpes\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_key.gd\" id=\"13_x2lbs\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_input_swizzle.gd\" id=\"14_2r6td\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_46a3k\"]\nscript = ExtResource(\"2_qlocd\")\naxis = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_0uhrt\"]\nscript = ExtResource(\"3_p61vs\")\n\n[sub_resource type=\"Resource\" id=\"Resource_eipue\"]\nscript = ExtResource(\"4_mho8x\")\nx = true\ny = true\nz = true\n\n[sub_resource type=\"Resource\" id=\"Resource_66vbo\"]\nscript = ExtResource(\"5_b42iu\")\nscale = Vector3(6.28, 1, 1)\napply_delta_time = false\n\n[sub_resource type=\"Resource\" id=\"Resource_wvbgw\"]\nscript = ExtResource(\"5_qvvu8\")\naction = ExtResource(\"4_jyoym\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_aqfvw\"]\nscript = ExtResource(\"4_6a0qh\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_46a3k\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_0uhrt\"), SubResource(\"Resource_eipue\"), SubResource(\"Resource_66vbo\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_wvbgw\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_g62j8\"]\nscript = ExtResource(\"5_4hkur\")\naction = ExtResource(\"1_8aom2\")\ninput_mappings = Array[ExtResource(\"4_6a0qh\")]([SubResource(\"Resource_aqfvw\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_jyaeo\"]\nscript = ExtResource(\"2_qlocd\")\naxis = 1\n\n[sub_resource type=\"Resource\" id=\"Resource_lo1xm\"]\nscript = ExtResource(\"3_p61vs\")\n\n[sub_resource type=\"Resource\" id=\"Resource_f41et\"]\nscript = ExtResource(\"4_mho8x\")\nx = true\ny = true\nz = true\n\n[sub_resource type=\"Resource\" id=\"Resource_os02k\"]\nscript = ExtResource(\"5_b42iu\")\nscale = Vector3(6.28, 1, 1)\napply_delta_time = false\n\n[sub_resource type=\"Resource\" id=\"Resource_t4dub\"]\nscript = ExtResource(\"5_qvvu8\")\naction = ExtResource(\"4_jyoym\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_jsmry\"]\nscript = ExtResource(\"4_6a0qh\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_jyaeo\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_lo1xm\"), SubResource(\"Resource_f41et\"), SubResource(\"Resource_os02k\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_t4dub\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_4sqcw\"]\nscript = ExtResource(\"5_4hkur\")\naction = ExtResource(\"7_swef3\")\ninput_mappings = Array[ExtResource(\"4_6a0qh\")]([SubResource(\"Resource_jsmry\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_ki4s5\"]\nscript = ExtResource(\"7_7w5i6\")\nbutton = 2\n\n[sub_resource type=\"Resource\" id=\"Resource_djtqh\"]\nscript = ExtResource(\"4_6a0qh\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_ki4s5\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_nyq6u\"]\nscript = ExtResource(\"5_4hkur\")\naction = ExtResource(\"4_jyoym\")\ninput_mappings = Array[ExtResource(\"4_6a0qh\")]([SubResource(\"Resource_djtqh\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_20kay\"]\nscript = ExtResource(\"13_x2lbs\")\nkey = 87\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_0syad\"]\nscript = ExtResource(\"14_2r6td\")\norder = 1\n\n[sub_resource type=\"Resource\" id=\"Resource_0bn66\"]\nscript = ExtResource(\"4_mho8x\")\nx = true\ny = true\nz = true\n\n[sub_resource type=\"Resource\" id=\"Resource_7gp34\"]\nscript = ExtResource(\"4_6a0qh\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_20kay\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_0syad\"), SubResource(\"Resource_0bn66\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_v2uh1\"]\nscript = ExtResource(\"13_x2lbs\")\nkey = 83\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_axfl8\"]\nscript = ExtResource(\"14_2r6td\")\norder = 1\n\n[sub_resource type=\"Resource\" id=\"Resource_vij8n\"]\nscript = ExtResource(\"4_6a0qh\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_v2uh1\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_axfl8\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_rauax\"]\nscript = ExtResource(\"13_x2lbs\")\nkey = 65\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_8xh6c\"]\nscript = ExtResource(\"4_mho8x\")\nx = true\ny = true\nz = true\n\n[sub_resource type=\"Resource\" id=\"Resource_8urnd\"]\nscript = ExtResource(\"4_6a0qh\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_rauax\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_8xh6c\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_utvoa\"]\nscript = ExtResource(\"13_x2lbs\")\nkey = 68\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_tj7qw\"]\nscript = ExtResource(\"4_6a0qh\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_utvoa\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_mlu0g\"]\nscript = ExtResource(\"5_4hkur\")\naction = ExtResource(\"12_v8r8p\")\ninput_mappings = Array[ExtResource(\"4_6a0qh\")]([SubResource(\"Resource_7gp34\"), SubResource(\"Resource_vij8n\"), SubResource(\"Resource_8urnd\"), SubResource(\"Resource_tj7qw\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_hh3gq\"]\nscript = ExtResource(\"2_nomn0\")\n\n[sub_resource type=\"Resource\" id=\"Resource_nkih0\"]\nscript = ExtResource(\"3_uds3g\")\nmax_depth = 1000.0\ncollide_with_areas = false\ncollision_mask = 1\n\n[sub_resource type=\"Resource\" id=\"Resource_wvei4\"]\nscript = ExtResource(\"4_6a0qh\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_hh3gq\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_nkih0\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_6b6vt\"]\nscript = ExtResource(\"5_4hkur\")\naction = ExtResource(\"9_rxgkr\")\ninput_mappings = Array[ExtResource(\"4_6a0qh\")]([SubResource(\"Resource_wvei4\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_lawue\"]\nscript = ExtResource(\"7_7w5i6\")\nbutton = 1\n\n[sub_resource type=\"Resource\" id=\"Resource_tvue7\"]\nscript = ExtResource(\"8_5glyu\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_m30sl\"]\nscript = ExtResource(\"4_6a0qh\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_lawue\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_tvue7\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_8y4cb\"]\nscript = ExtResource(\"5_4hkur\")\naction = ExtResource(\"12_wmpes\")\ninput_mappings = Array[ExtResource(\"4_6a0qh\")]([SubResource(\"Resource_m30sl\")])\n\n[resource]\nscript = ExtResource(\"1_rde8c\")\ndisplay_name = \"\"\nmappings = Array[ExtResource(\"5_4hkur\")]([SubResource(\"Resource_g62j8\"), SubResource(\"Resource_4sqcw\"), SubResource(\"Resource_nyq6u\"), SubResource(\"Resource_mlu0g\"), SubResource(\"Resource_6b6vt\"), SubResource(\"Resource_8y4cb\")])\n"
  },
  {
    "path": "guide_examples/mouse_position_3d/mou4D8E.tmp",
    "content": "[gd_scene load_steps=21 format=3 uid=\"uid://dmpv0dh2nk5j\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/mouse_position_3d/mouse_position_3d.gd\" id=\"1_8oqik\"]\n[ext_resource type=\"Resource\" uid=\"uid://bpatu7vi2kj4l\" path=\"res://guide_examples/mouse_position_3d/mapping_context/mouse_position_3d.tres\" id=\"2_bb21n\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/mouse_position_3d/camera_control.gd\" id=\"3_4etic\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://dkr80d2pi0d41\" path=\"res://addons/guide/debugger/guide_debugger.tscn\" id=\"3_m2gj2\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/mouse_position_3d/mouse_indicator.gd\" id=\"3_tunsg\"]\n[ext_resource type=\"Resource\" uid=\"uid://2b0ncq7ogvv0\" path=\"res://guide_examples/mouse_position_3d/mapping_context/actions/cursor_3d.tres\" id=\"4_dla1l\"]\n[ext_resource type=\"Resource\" uid=\"uid://bago2pmgvpepb\" path=\"res://guide_examples/mouse_position_3d/mapping_context/actions/camera_pitch.tres\" id=\"4_xr1vq\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/mouse_position_3d/player.gd\" id=\"5_8nflt\"]\n[ext_resource type=\"Resource\" uid=\"uid://bltmi1lr7umq0\" path=\"res://guide_examples/mouse_position_3d/mapping_context/actions/camera_yaw.tres\" id=\"5_mnyiu\"]\n[ext_resource type=\"Resource\" uid=\"uid://cpljlaavuq515\" path=\"res://guide_examples/mouse_position_3d/mapping_context/actions/camera_toggle.tres\" id=\"6_51csy\"]\n[ext_resource type=\"Theme\" uid=\"uid://dot0gi1yoqmrl\" path=\"res://guide_examples/shared/ui_theme.tres\" id=\"6_lyxvk\"]\n[ext_resource type=\"Resource\" uid=\"uid://b2jb27062t2mh\" path=\"res://guide_examples/mouse_position_3d/mapping_context/actions/select.tres\" id=\"6_uviri\"]\n[ext_resource type=\"Resource\" uid=\"uid://ceti1avx1l1qw\" path=\"res://guide_examples/mouse_position_3d/mapping_context/actions/camera_move.tres\" id=\"7_1vbs8\"]\n\n[sub_resource type=\"NavigationMesh\" id=\"NavigationMesh_sfop5\"]\nvertices = PackedVector3Array(2.25, 2.656, -7, 2.25, 2.656, -3.5, 4.25, 1.156, -3.5, 4.25, 1.156, -7, 4.25, 1.156, -7, 4.25, 1.156, -3.5, 4.75, 0.905995, -2.5, 9, 0.905995, -2.5, 9, 0.905995, -9, 4.75, 0.905995, -8.5, 9, 0.905995, -9, 4.25, 0.905995, -9, 4.75, 0.905995, -8.5, -3.5, 3.656, -3.5, 2.25, 2.656, -3.5, 2.25, 2.656, -7, -7.5, 3.656, -7, 0.8125, 3.656, -3.5, 0.857143, 3.656, -7, -7.5, 3.656, 5.25, -4.25, 3.656, 5.25, -4.25, 3.656, -2.75, -7.5, 3.656, -7, -4.25, 3.656, -2.75, -3.5, 3.656, -3.5, -7.5, 3.656, -7, 9, 0.905995, -2.5, 4.75, 0.905995, -2.5, 4.25, 0.905995, -1.5, -3, 0.905995, 7.25, -9, 0.905995, 7, -9, 0.905995, 9, 9, 0.905995, 9, 4.25, 0.905995, -1.5, -2.25, 0.905995, -1.5, -2.25, 0.905995, 6.5, 9, 0.905995, 9, 9, 0.905995, -2.5, 4.25, 0.905995, -1.5, -2.25, 0.905995, 6.5, -2.25, 0.905995, 6.5, -3, 0.905995, 7.25, 9, 0.905995, 9)\npolygons = [PackedInt32Array(3, 2, 0), PackedInt32Array(0, 2, 1), PackedInt32Array(5, 4, 6), PackedInt32Array(6, 4, 9), PackedInt32Array(6, 9, 7), PackedInt32Array(7, 9, 8), PackedInt32Array(10, 12, 11), PackedInt32Array(15, 14, 18), PackedInt32Array(18, 14, 17), PackedInt32Array(18, 17, 13), PackedInt32Array(18, 13, 16), PackedInt32Array(20, 19, 21), PackedInt32Array(21, 19, 22), PackedInt32Array(25, 24, 23), PackedInt32Array(28, 27, 26), PackedInt32Array(30, 29, 31), PackedInt32Array(31, 29, 32), PackedInt32Array(35, 34, 33), PackedInt32Array(37, 36, 38), PackedInt32Array(38, 36, 39), PackedInt32Array(42, 41, 40)]\nagent_radius = 1.0\n\n[sub_resource type=\"ProceduralSkyMaterial\" id=\"ProceduralSkyMaterial_taqbr\"]\n\n[sub_resource type=\"Sky\" id=\"Sky_qlesy\"]\nsky_material = SubResource(\"ProceduralSkyMaterial_taqbr\")\n\n[sub_resource type=\"Environment\" id=\"Environment_wd3mo\"]\nbackground_mode = 2\nsky = SubResource(\"Sky_qlesy\")\n\n[sub_resource type=\"StandardMaterial3D\" id=\"StandardMaterial3D_q83ll\"]\ntransparency = 1\nalbedo_color = Color(0.890196, 0.176471, 0.133333, 0.678431)\n\n[sub_resource type=\"StandardMaterial3D\" id=\"StandardMaterial3D_ubo0r\"]\nalbedo_color = Color(1.15514e-06, 0.522721, 0.747218, 1)\n\n[sub_resource type=\"CapsuleShape3D\" id=\"CapsuleShape3D_3a636\"]\nradius = 0.6\nheight = 2.3\n\n[node name=\"MousePosition3d\" type=\"Node3D\"]\nscript = ExtResource(\"1_8oqik\")\nmapping_context = ExtResource(\"2_bb21n\")\n\n[node name=\"CameraYaw\" type=\"Node3D\" parent=\".\"]\nunique_name_in_owner = true\ntransform = Transform3D(0.435231, 0, 0.900319, 0, 1, 0, -0.900319, 0, 0.435231, 6.62904, 4.72731, 0)\nscript = ExtResource(\"3_4etic\")\ncamera_pitch = ExtResource(\"4_xr1vq\")\ncamera_yaw = ExtResource(\"5_mnyiu\")\ncamera_toggle = ExtResource(\"6_51csy\")\ncamera_move = ExtResource(\"7_1vbs8\")\nmovement_speed = 8.0\n\n[node name=\"CameraPitch\" type=\"SpringArm3D\" parent=\"CameraYaw\"]\nunique_name_in_owner = true\ntransform = Transform3D(1, 0, 0, 0, 0.984808, 0.173648, 0, -0.173648, 0.984808, 0, 0, 0)\nspring_length = 8.0\n\n[node name=\"Camera3D\" type=\"Camera3D\" parent=\"CameraYaw/CameraPitch\"]\n\n[node name=\"World\" type=\"Node3D\" parent=\".\"]\n\n[node name=\"NavigationRegion3D\" type=\"NavigationRegion3D\" parent=\"World\"]\nnavigation_mesh = SubResource(\"NavigationMesh_sfop5\")\n\n[node name=\"CSGBox3D\" type=\"CSGBox3D\" parent=\"World/NavigationRegion3D\"]\nuse_collision = true\nsize = Vector3(20, 1, 20)\n\n[node name=\"CSGBox3D2\" type=\"CSGBox3D\" parent=\"World/NavigationRegion3D\"]\ntransform = Transform3D(0.80368, 0.595061, 0, -0.595061, 0.80368, 0, 0, 0, 1, 2.07477, 0.832275, -5.22408)\nuse_collision = true\nsize = Vector3(5.14954, 2.2251, 5.07178)\n\n[node name=\"CSGBox3D3\" type=\"CSGBox3D\" parent=\"World/NavigationRegion3D\"]\ntransform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.8367, 2.1428, -5.22408)\nuse_collision = true\nsize = Vector3(9.01666, 2.2251, 5.07178)\n\n[node name=\"CSGBox3D4\" type=\"CSGBox3D\" parent=\"World/NavigationRegion3D\"]\ntransform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -5.81752, 2.1428, 1.66142)\nuse_collision = true\nsize = Vector3(9.01666, 2.2251, 5.07178)\n\n[node name=\"WorldEnvironment\" type=\"WorldEnvironment\" parent=\"World\"]\nenvironment = SubResource(\"Environment_wd3mo\")\n\n[node name=\"DirectionalLight3D\" type=\"DirectionalLight3D\" parent=\"World\"]\ntransform = Transform3D(1, 0, 0, 0, 0.780496, 0.625161, 0, -0.625161, 0.780496, 0, 4.93937, 0)\nshadow_enabled = true\n\n[node name=\"MouseIndicator\" type=\"CSGSphere3D\" parent=\".\"]\ntransform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.84397, 1.04571, 0)\nmaterial = SubResource(\"StandardMaterial3D_q83ll\")\nscript = ExtResource(\"3_tunsg\")\ncursor = ExtResource(\"4_dla1l\")\ncamera_toggle = ExtResource(\"6_51csy\")\n\n[node name=\"CharacterBody3D\" type=\"CharacterBody3D\" parent=\".\"]\ntransform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.26495, 0)\ncollision_layer = 2\ninput_ray_pickable = false\nfloor_constant_speed = true\nscript = ExtResource(\"5_8nflt\")\nselect = ExtResource(\"6_uviri\")\ncursor = ExtResource(\"4_dla1l\")\n\n[node name=\"CSGCylinder3D\" type=\"CSGCylinder3D\" parent=\"CharacterBody3D\"]\ntransform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.28249, 0)\nmaterial = SubResource(\"StandardMaterial3D_ubo0r\")\n\n[node name=\"CollisionShape3D\" type=\"CollisionShape3D\" parent=\"CharacterBody3D\"]\ntransform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.26098, 0)\nshape = SubResource(\"CapsuleShape3D_3a636\")\n\n[node name=\"NavigationAgent3D\" type=\"NavigationAgent3D\" parent=\"CharacterBody3D\"]\nunique_name_in_owner = true\ndebug_enabled = true\ndebug_use_custom = true\ndebug_path_custom_color = Color(0.886095, 0.359614, 0.933159, 1)\ndebug_path_custom_point_size = 7.17\n\n[node name=\"CanvasLayer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"GuideDebugger\" parent=\"CanvasLayer\" instance=ExtResource(\"3_m2gj2\")]\ntheme = ExtResource(\"6_lyxvk\")\n"
  },
  {
    "path": "guide_examples/mouse_position_3d/mouse_indicator.gd",
    "content": "extends CSGSphere3D\n\n@export var cursor:GUIDEAction\n@export var camera_toggle:GUIDEAction\n\nfunc _process(_delta:float) -> void:\n\tvar new_pos := cursor.value_axis_3d\n\tif not new_pos.is_finite() or camera_toggle.is_triggered():\n\t\tvisible = false\n\t\treturn\n\t\t\n\tvisible = true\n\tglobal_position = new_pos\n"
  },
  {
    "path": "guide_examples/mouse_position_3d/mouse_indicator.gd.uid",
    "content": "uid://mj7s4e2n6fpm\n"
  },
  {
    "path": "guide_examples/mouse_position_3d/mouse_position_3d.gd",
    "content": "extends Node3D\n\n\n@export var mapping_context:GUIDEMappingContext\n\nfunc _ready() -> void:\n\tGUIDE.enable_mapping_context(mapping_context)\n"
  },
  {
    "path": "guide_examples/mouse_position_3d/mouse_position_3d.gd.uid",
    "content": "uid://5x7dcnegcvlb\n"
  },
  {
    "path": "guide_examples/mouse_position_3d/mouse_position_3d.tscn",
    "content": "[gd_scene load_steps=22 format=3 uid=\"uid://dmpv0dh2nk5j\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/mouse_position_3d/mouse_position_3d.gd\" id=\"1_8oqik\"]\n[ext_resource type=\"Resource\" uid=\"uid://bpatu7vi2kj4l\" path=\"res://guide_examples/mouse_position_3d/mapping_context/mouse_position_3d.tres\" id=\"2_bb21n\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/mouse_position_3d/camera_control.gd\" id=\"3_4etic\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://dkr80d2pi0d41\" path=\"res://addons/guide/debugger/guide_debugger.tscn\" id=\"3_m2gj2\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/mouse_position_3d/mouse_indicator.gd\" id=\"3_tunsg\"]\n[ext_resource type=\"Resource\" uid=\"uid://2b0ncq7ogvv0\" path=\"res://guide_examples/mouse_position_3d/mapping_context/actions/cursor_3d.tres\" id=\"4_dla1l\"]\n[ext_resource type=\"Resource\" uid=\"uid://bago2pmgvpepb\" path=\"res://guide_examples/mouse_position_3d/mapping_context/actions/camera_pitch.tres\" id=\"4_xr1vq\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/mouse_position_3d/player.gd\" id=\"5_8nflt\"]\n[ext_resource type=\"Resource\" uid=\"uid://bltmi1lr7umq0\" path=\"res://guide_examples/mouse_position_3d/mapping_context/actions/camera_yaw.tres\" id=\"5_mnyiu\"]\n[ext_resource type=\"Resource\" uid=\"uid://cpljlaavuq515\" path=\"res://guide_examples/mouse_position_3d/mapping_context/actions/camera_toggle.tres\" id=\"6_51csy\"]\n[ext_resource type=\"Theme\" uid=\"uid://dot0gi1yoqmrl\" path=\"res://guide_examples/shared/ui_theme.tres\" id=\"6_lyxvk\"]\n[ext_resource type=\"Resource\" uid=\"uid://b2jb27062t2mh\" path=\"res://guide_examples/mouse_position_3d/mapping_context/actions/select.tres\" id=\"6_uviri\"]\n[ext_resource type=\"Resource\" uid=\"uid://ceti1avx1l1qw\" path=\"res://guide_examples/mouse_position_3d/mapping_context/actions/camera_move.tres\" id=\"7_1vbs8\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/shared/instructions_label.gd\" id=\"12_c4bk3\"]\n\n[sub_resource type=\"NavigationMesh\" id=\"NavigationMesh_sfop5\"]\nvertices = PackedVector3Array(2.25, 2.656, -7, 2.25, 2.656, -3.5, 4.25, 1.156, -3.5, 4.25, 1.156, -7, 4.25, 1.156, -7, 4.25, 1.156, -3.5, 4.75, 0.905995, -2.5, 9, 0.905995, -2.5, 9, 0.905995, -9, 4.75, 0.905995, -8.5, 9, 0.905995, -9, 4.25, 0.905995, -9, 4.75, 0.905995, -8.5, -3.5, 3.656, -3.5, 2.25, 2.656, -3.5, 2.25, 2.656, -7, -7.5, 3.656, -7, 0.8125, 3.656, -3.5, 0.857143, 3.656, -7, -7.5, 3.656, 5.25, -4.25, 3.656, 5.25, -4.25, 3.656, -2.75, -7.5, 3.656, -7, -4.25, 3.656, -2.75, -3.5, 3.656, -3.5, -7.5, 3.656, -7, 9, 0.905995, -2.5, 4.75, 0.905995, -2.5, 4.25, 0.905995, -1.5, -3, 0.905995, 7.25, -9, 0.905995, 7, -9, 0.905995, 9, 9, 0.905995, 9, 4.25, 0.905995, -1.5, -2.25, 0.905995, -1.5, -2.25, 0.905995, 6.5, 9, 0.905995, 9, 9, 0.905995, -2.5, 4.25, 0.905995, -1.5, -2.25, 0.905995, 6.5, -2.25, 0.905995, 6.5, -3, 0.905995, 7.25, 9, 0.905995, 9)\npolygons = [PackedInt32Array(3, 2, 0), PackedInt32Array(0, 2, 1), PackedInt32Array(5, 4, 6), PackedInt32Array(6, 4, 9), PackedInt32Array(6, 9, 7), PackedInt32Array(7, 9, 8), PackedInt32Array(10, 12, 11), PackedInt32Array(15, 14, 18), PackedInt32Array(18, 14, 17), PackedInt32Array(18, 17, 13), PackedInt32Array(18, 13, 16), PackedInt32Array(20, 19, 21), PackedInt32Array(21, 19, 22), PackedInt32Array(25, 24, 23), PackedInt32Array(28, 27, 26), PackedInt32Array(30, 29, 31), PackedInt32Array(31, 29, 32), PackedInt32Array(35, 34, 33), PackedInt32Array(37, 36, 38), PackedInt32Array(38, 36, 39), PackedInt32Array(42, 41, 40)]\nagent_radius = 1.0\n\n[sub_resource type=\"ProceduralSkyMaterial\" id=\"ProceduralSkyMaterial_taqbr\"]\n\n[sub_resource type=\"Sky\" id=\"Sky_qlesy\"]\nsky_material = SubResource(\"ProceduralSkyMaterial_taqbr\")\n\n[sub_resource type=\"Environment\" id=\"Environment_wd3mo\"]\nbackground_mode = 2\nsky = SubResource(\"Sky_qlesy\")\n\n[sub_resource type=\"StandardMaterial3D\" id=\"StandardMaterial3D_q83ll\"]\ntransparency = 1\nalbedo_color = Color(0.890196, 0.176471, 0.133333, 0.678431)\n\n[sub_resource type=\"StandardMaterial3D\" id=\"StandardMaterial3D_ubo0r\"]\nalbedo_color = Color(1.15514e-06, 0.522721, 0.747218, 1)\n\n[sub_resource type=\"CapsuleShape3D\" id=\"CapsuleShape3D_3a636\"]\nradius = 0.6\nheight = 2.3\n\n[node name=\"MousePosition3d\" type=\"Node3D\"]\nscript = ExtResource(\"1_8oqik\")\nmapping_context = ExtResource(\"2_bb21n\")\n\n[node name=\"CameraYaw\" type=\"Node3D\" parent=\".\"]\nunique_name_in_owner = true\ntransform = Transform3D(0.435231, 0, 0.900319, 0, 1, 0, -0.900319, 0, 0.435231, 6.62904, 4.72731, 0)\nscript = ExtResource(\"3_4etic\")\ncamera_pitch = ExtResource(\"4_xr1vq\")\ncamera_yaw = ExtResource(\"5_mnyiu\")\ncamera_toggle = ExtResource(\"6_51csy\")\ncamera_move = ExtResource(\"7_1vbs8\")\nmovement_speed = 8.0\n\n[node name=\"CameraPitch\" type=\"SpringArm3D\" parent=\"CameraYaw\"]\nunique_name_in_owner = true\ntransform = Transform3D(1, 0, 0, 0, 0.984808, 0.173648, 0, -0.173648, 0.984808, 0, 0, 0)\nspring_length = 8.0\n\n[node name=\"Camera3D\" type=\"Camera3D\" parent=\"CameraYaw/CameraPitch\"]\n\n[node name=\"World\" type=\"Node3D\" parent=\".\"]\n\n[node name=\"NavigationRegion3D\" type=\"NavigationRegion3D\" parent=\"World\"]\nnavigation_mesh = SubResource(\"NavigationMesh_sfop5\")\n\n[node name=\"CSGBox3D\" type=\"CSGBox3D\" parent=\"World/NavigationRegion3D\"]\nuse_collision = true\nsize = Vector3(20, 1, 20)\n\n[node name=\"CSGBox3D2\" type=\"CSGBox3D\" parent=\"World/NavigationRegion3D\"]\ntransform = Transform3D(0.80368, 0.595061, 0, -0.595061, 0.80368, 0, 0, 0, 1, 2.07477, 0.832275, -5.22408)\nuse_collision = true\nsize = Vector3(5.14954, 2.2251, 5.07178)\n\n[node name=\"CSGBox3D3\" type=\"CSGBox3D\" parent=\"World/NavigationRegion3D\"]\ntransform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.8367, 2.1428, -5.22408)\nuse_collision = true\nsize = Vector3(9.01666, 2.2251, 5.07178)\n\n[node name=\"CSGBox3D4\" type=\"CSGBox3D\" parent=\"World/NavigationRegion3D\"]\ntransform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -5.81752, 2.1428, 1.66142)\nuse_collision = true\nsize = Vector3(9.01666, 2.2251, 5.07178)\n\n[node name=\"WorldEnvironment\" type=\"WorldEnvironment\" parent=\"World\"]\nenvironment = SubResource(\"Environment_wd3mo\")\n\n[node name=\"DirectionalLight3D\" type=\"DirectionalLight3D\" parent=\"World\"]\ntransform = Transform3D(1, 0, 0, 0, 0.780496, 0.625161, 0, -0.625161, 0.780496, 0, 4.93937, 0)\nshadow_enabled = true\n\n[node name=\"MouseIndicator\" type=\"CSGSphere3D\" parent=\".\"]\ntransform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.84397, 1.04571, 0)\nmaterial = SubResource(\"StandardMaterial3D_q83ll\")\nscript = ExtResource(\"3_tunsg\")\ncursor = ExtResource(\"4_dla1l\")\ncamera_toggle = ExtResource(\"6_51csy\")\n\n[node name=\"CharacterBody3D\" type=\"CharacterBody3D\" parent=\".\"]\ntransform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.26495, 0)\ncollision_layer = 2\ninput_ray_pickable = false\nfloor_constant_speed = true\nscript = ExtResource(\"5_8nflt\")\nselect = ExtResource(\"6_uviri\")\ncursor = ExtResource(\"4_dla1l\")\n\n[node name=\"CSGCylinder3D\" type=\"CSGCylinder3D\" parent=\"CharacterBody3D\"]\ntransform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.28249, 0)\nmaterial = SubResource(\"StandardMaterial3D_ubo0r\")\n\n[node name=\"CollisionShape3D\" type=\"CollisionShape3D\" parent=\"CharacterBody3D\"]\ntransform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.26098, 0)\nshape = SubResource(\"CapsuleShape3D_3a636\")\n\n[node name=\"NavigationAgent3D\" type=\"NavigationAgent3D\" parent=\"CharacterBody3D\"]\nunique_name_in_owner = true\ndebug_enabled = true\ndebug_use_custom = true\ndebug_path_custom_color = Color(0.886095, 0.359614, 0.933159, 1)\ndebug_path_custom_point_size = 7.17\n\n[node name=\"UILayer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"RichTextLabel\" type=\"RichTextLabel\" parent=\"UILayer\"]\nanchors_preset = 1\nanchor_left = 1.0\nanchor_right = 1.0\noffset_left = -104.0\noffset_top = 56.0\noffset_right = -64.0\noffset_bottom = 96.0\ngrow_horizontal = 0\ntheme = ExtResource(\"6_lyxvk\")\nscript = ExtResource(\"12_c4bk3\")\ninstructions_text = \"%s to send the agent somewhere.\n%s to move the camera.\n%s to rotate the camera.\n%s to tilt the camera.\"\nactions = Array[Resource(\"res://addons/guide/guide_action.gd\")]([ExtResource(\"6_uviri\"), ExtResource(\"7_1vbs8\"), ExtResource(\"5_mnyiu\"), ExtResource(\"4_xr1vq\")])\n\n[node name=\"DebuggerLayer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"GuideDebugger\" parent=\"DebuggerLayer\" instance=ExtResource(\"3_m2gj2\")]\ntheme = ExtResource(\"6_lyxvk\")\n"
  },
  {
    "path": "guide_examples/mouse_position_3d/player.gd",
    "content": "extends CharacterBody3D\n\n@export var select:GUIDEAction\n@export var cursor:GUIDEAction\n@export var speed:float = 5.0\n\n@onready var _navigation_agent_3d:NavigationAgent3D = %NavigationAgent3D\n\nfunc _ready() -> void:\n\tselect.triggered.connect(_new_destination)\n\nfunc _physics_process(_delta:float) -> void:\n\tif not _navigation_agent_3d.is_navigation_finished():\n\t\tvar next_pos := _navigation_agent_3d.get_next_path_position()\n\t\tvelocity = global_position.direction_to(next_pos) * speed\n\telse:\n\t\tvelocity = Vector3.ZERO\n\t\t\n\tif not is_on_floor():\n\t\tvelocity.y =  -9.18\n\t\n\tmove_and_slide()\n\nfunc _new_destination() -> void:\n\tvar destination := cursor.value_axis_3d\n\tif not destination.is_finite():\n\t\treturn\n\t_navigation_agent_3d.target_position = destination\n\t\t\n\t\t\n"
  },
  {
    "path": "guide_examples/mouse_position_3d/player.gd.uid",
    "content": "uid://kp2vh24w5jih\n"
  },
  {
    "path": "guide_examples/quick_start/game.gd",
    "content": "extends Node2D\n\n## The mapping context that we use\n@export var mapping_context:GUIDEMappingContext\n\nfunc _ready() -> void:\n\tGUIDE.enable_mapping_context(mapping_context)\n"
  },
  {
    "path": "guide_examples/quick_start/game.gd.uid",
    "content": "uid://ceq2p7x1uhe8x\n"
  },
  {
    "path": "guide_examples/quick_start/mapping_contexts/actions/move.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://uscuhd84vv0i\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_mqwm6\"]\n\n[resource]\nscript = ExtResource(\"1_mqwm6\")\nname = &\"\"\naction_value_type = 2\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/quick_start/mapping_contexts/actions/say_hi.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://d0dmecppsgpo6\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_ocdl4\"]\n\n[resource]\nscript = ExtResource(\"1_ocdl4\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/quick_start/mapping_contexts/quickstart.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMappingContext\" load_steps=27 format=3 uid=\"uid://b2becclfhsxec\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_mapping_context.gd\" id=\"1_1hcuh\"]\n[ext_resource type=\"Resource\" uid=\"uid://uscuhd84vv0i\" path=\"res://guide_examples/quick_start/mapping_contexts/actions/move.tres\" id=\"1_5uqll\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_key.gd\" id=\"2_xl7hk\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_negate.gd\" id=\"3_mr5va\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_input_swizzle.gd\" id=\"4_4a1ev\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_input_mapping.gd\" id=\"5_1m2pq\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action_mapping.gd\" id=\"6_ydpah\"]\n[ext_resource type=\"Resource\" uid=\"uid://d0dmecppsgpo6\" path=\"res://guide_examples/quick_start/mapping_contexts/actions/say_hi.tres\" id=\"7_wuqd4\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_pressed.gd\" id=\"8_khp4m\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_f0kb3\"]\nscript = ExtResource(\"2_xl7hk\")\nkey = 87\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_fvwu2\"]\nscript = ExtResource(\"3_mr5va\")\nx = true\ny = true\nz = true\n\n[sub_resource type=\"Resource\" id=\"Resource_xg24o\"]\nscript = ExtResource(\"4_4a1ev\")\norder = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_cgtrg\"]\nscript = ExtResource(\"5_1m2pq\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_f0kb3\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_fvwu2\"), SubResource(\"Resource_xg24o\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_i7s5n\"]\nscript = ExtResource(\"2_xl7hk\")\nkey = 83\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_6l7af\"]\nscript = ExtResource(\"4_4a1ev\")\norder = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_o72ru\"]\nscript = ExtResource(\"5_1m2pq\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_i7s5n\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_6l7af\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_4kbei\"]\nscript = ExtResource(\"2_xl7hk\")\nkey = 65\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_t12km\"]\nscript = ExtResource(\"3_mr5va\")\nx = true\ny = true\nz = true\n\n[sub_resource type=\"Resource\" id=\"Resource_3xdyg\"]\nscript = ExtResource(\"5_1m2pq\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_4kbei\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_t12km\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_uld1v\"]\nscript = ExtResource(\"2_xl7hk\")\nkey = 68\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_fhxmh\"]\nscript = ExtResource(\"5_1m2pq\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_uld1v\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_y44kn\"]\nscript = ExtResource(\"6_ydpah\")\naction = ExtResource(\"1_5uqll\")\ninput_mappings = Array[ExtResource(\"5_1m2pq\")]([SubResource(\"Resource_cgtrg\"), SubResource(\"Resource_o72ru\"), SubResource(\"Resource_3xdyg\"), SubResource(\"Resource_fhxmh\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_cl30n\"]\nscript = ExtResource(\"2_xl7hk\")\nkey = 32\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_31mjc\"]\nscript = ExtResource(\"8_khp4m\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_dq2sf\"]\nscript = ExtResource(\"5_1m2pq\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_cl30n\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_31mjc\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_7p43t\"]\nscript = ExtResource(\"6_ydpah\")\naction = ExtResource(\"7_wuqd4\")\ninput_mappings = Array[ExtResource(\"5_1m2pq\")]([SubResource(\"Resource_dq2sf\")])\n\n[resource]\nscript = ExtResource(\"1_1hcuh\")\ndisplay_name = \"\"\nmappings = Array[ExtResource(\"6_ydpah\")]([SubResource(\"Resource_y44kn\"), SubResource(\"Resource_7p43t\")])\n"
  },
  {
    "path": "guide_examples/quick_start/player.gd",
    "content": "extends Sprite2D\n\n## The speed at which the player moves.\n@export var speed:float = 300\n## The action that moves the player.\n@export var move_action:GUIDEAction\n## The action that says hi.\n@export var say_hi_action:GUIDEAction\n\nfunc _ready() -> void:\n\t# Call the `say_hi` function whenever the say_hi_action is triggered.\n\tsay_hi_action.triggered.connect(_say_hi)\n\nfunc _say_hi() -> void:\n\t# Quickly show and hide message panel\n\t%MessagePanel.visible = true\n\tawait get_tree().create_timer(0.5).timeout\n\t%MessagePanel.visible = false\n\t\nfunc _process(delta:float) -> void:\n\t# Get the input value from the action and move the player.\n\tposition += move_action.value_axis_2d * speed * delta\n"
  },
  {
    "path": "guide_examples/quick_start/player.gd.uid",
    "content": "uid://c0b6whgglitqe\n"
  },
  {
    "path": "guide_examples/quick_start/quick_start.tscn",
    "content": "[gd_scene load_steps=7 format=3 uid=\"uid://cye0mxa62e7lh\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/quick_start/game.gd\" id=\"1_eetgd\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://byjxtsekdl8t2\" path=\"res://guide_examples/shared/godot_logo.svg\" id=\"1_twtcc\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/quick_start/player.gd\" id=\"2_75sqh\"]\n[ext_resource type=\"Resource\" uid=\"uid://b2becclfhsxec\" path=\"res://guide_examples/quick_start/mapping_contexts/quickstart.tres\" id=\"2_enypg\"]\n[ext_resource type=\"Resource\" uid=\"uid://uscuhd84vv0i\" path=\"res://guide_examples/quick_start/mapping_contexts/actions/move.tres\" id=\"3_nwedj\"]\n[ext_resource type=\"Resource\" uid=\"uid://d0dmecppsgpo6\" path=\"res://guide_examples/quick_start/mapping_contexts/actions/say_hi.tres\" id=\"6_xqef5\"]\n\n[node name=\"QuickStart\" type=\"Node2D\"]\nscript = ExtResource(\"1_eetgd\")\nmapping_context = ExtResource(\"2_enypg\")\n\n[node name=\"Player\" type=\"Sprite2D\" parent=\".\"]\nposition = Vector2(979, 544)\ntexture = ExtResource(\"1_twtcc\")\nscript = ExtResource(\"2_75sqh\")\nmove_action = ExtResource(\"3_nwedj\")\nsay_hi_action = ExtResource(\"6_xqef5\")\n\n[node name=\"MessagePanel\" type=\"PanelContainer\" parent=\"Player\"]\nunique_name_in_owner = true\nvisible = false\noffset_left = 38.0\noffset_top = -161.0\noffset_right = 122.0\noffset_bottom = -73.0\n\n[node name=\"Label\" type=\"Label\" parent=\"Player/MessagePanel\"]\nlayout_mode = 2\ntheme_override_font_sizes/font_size = 64\ntext = \"Hi!\"\n"
  },
  {
    "path": "guide_examples/remapping/mapping_contexts/binding_controller.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMappingContext\" load_steps=33 format=3 uid=\"uid://dubuepcs1w17f\"]\n\n[ext_resource type=\"Resource\" uid=\"uid://bohjda85owgnc\" path=\"res://guide_examples/remapping/mapping_contexts/controller_actions/ui_accept.tres\" id=\"1_rokdq\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_joy_button.gd\" id=\"2_30snk\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_input_mapping.gd\" id=\"3_ifcmr\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_pressed.gd\" id=\"4_bn4su\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action_mapping.gd\" id=\"5_wtinc\"]\n[ext_resource type=\"Resource\" uid=\"uid://bcum2m26we6ct\" path=\"res://guide_examples/remapping/mapping_contexts/shared_actions/close_menu.tres\" id=\"6_mbh1l\"]\n[ext_resource type=\"Resource\" uid=\"uid://ce3ytxn2tcxxe\" path=\"res://guide_examples/remapping/mapping_contexts/controller_actions/switch_to_keyboard.tres\" id=\"7_e2y7c\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_released.gd\" id=\"7_ofh8l\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_any.gd\" id=\"8_qiw4m\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_mapping_context.gd\" id=\"9_rcbwk\"]\n[ext_resource type=\"Resource\" uid=\"uid://dg1or0do0s1ad\" path=\"res://guide_examples/remapping/mapping_contexts/controller_actions/next_tab.tres\" id=\"10_5tveu\"]\n[ext_resource type=\"Resource\" uid=\"uid://cfrx54l1vmjey\" path=\"res://guide_examples/remapping/mapping_contexts/controller_actions/previous_tab.tres\" id=\"11_iu0wm\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_uri0r\"]\nscript = ExtResource(\"2_30snk\")\nbutton = 0\njoy_index = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_i518v\"]\nscript = ExtResource(\"7_ofh8l\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_kpld2\"]\nscript = ExtResource(\"3_ifcmr\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_uri0r\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_i518v\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_33xax\"]\nscript = ExtResource(\"5_wtinc\")\naction = ExtResource(\"1_rokdq\")\ninput_mappings = Array[ExtResource(\"3_ifcmr\")]([SubResource(\"Resource_kpld2\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_lu1fr\"]\nscript = ExtResource(\"2_30snk\")\nbutton = 6\njoy_index = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_oikmn\"]\nscript = ExtResource(\"7_ofh8l\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_lju6e\"]\nscript = ExtResource(\"3_ifcmr\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_lu1fr\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_oikmn\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_djru6\"]\nscript = ExtResource(\"5_wtinc\")\naction = ExtResource(\"6_mbh1l\")\ninput_mappings = Array[ExtResource(\"3_ifcmr\")]([SubResource(\"Resource_lju6e\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_mc0ue\"]\nscript = ExtResource(\"8_qiw4m\")\nmouse_buttons = true\nmouse_movement = false\nminimum_mouse_movement_distance = 1.0\njoy_buttons = false\njoy_axes = false\nminimum_joy_axis_actuation_strength = 0.2\nkeyboard = true\ntouch = false\nmouse = true\njoy = false\n\n[sub_resource type=\"Resource\" id=\"Resource_vsgae\"]\nscript = ExtResource(\"4_bn4su\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_vwjgy\"]\nscript = ExtResource(\"3_ifcmr\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_mc0ue\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_vsgae\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_38x5y\"]\nscript = ExtResource(\"5_wtinc\")\naction = ExtResource(\"7_e2y7c\")\ninput_mappings = Array[ExtResource(\"3_ifcmr\")]([SubResource(\"Resource_vwjgy\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_sudy1\"]\nscript = ExtResource(\"2_30snk\")\nbutton = 10\njoy_index = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_4j53a\"]\nscript = ExtResource(\"4_bn4su\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_1g85d\"]\nscript = ExtResource(\"3_ifcmr\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_sudy1\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_4j53a\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_qsusb\"]\nscript = ExtResource(\"5_wtinc\")\naction = ExtResource(\"10_5tveu\")\ninput_mappings = Array[ExtResource(\"3_ifcmr\")]([SubResource(\"Resource_1g85d\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_dkk46\"]\nscript = ExtResource(\"2_30snk\")\nbutton = 9\njoy_index = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_taj0d\"]\nscript = ExtResource(\"4_bn4su\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_35h1x\"]\nscript = ExtResource(\"3_ifcmr\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_dkk46\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_taj0d\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_rl360\"]\nscript = ExtResource(\"5_wtinc\")\naction = ExtResource(\"11_iu0wm\")\ninput_mappings = Array[ExtResource(\"3_ifcmr\")]([SubResource(\"Resource_35h1x\")])\n\n[resource]\nscript = ExtResource(\"9_rcbwk\")\ndisplay_name = \"\"\nmappings = Array[ExtResource(\"5_wtinc\")]([SubResource(\"Resource_33xax\"), SubResource(\"Resource_djru6\"), SubResource(\"Resource_38x5y\"), SubResource(\"Resource_qsusb\"), SubResource(\"Resource_rl360\")])\n"
  },
  {
    "path": "guide_examples/remapping/mapping_contexts/binding_keyboard.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMappingContext\" load_steps=18 format=3 uid=\"uid://bqd45wwsetlyg\"]\n\n[ext_resource type=\"Resource\" uid=\"uid://bcum2m26we6ct\" path=\"res://guide_examples/remapping/mapping_contexts/shared_actions/close_menu.tres\" id=\"1_j25bp\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_key.gd\" id=\"2_a3vaw\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_input_mapping.gd\" id=\"3_n037t\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_released.gd\" id=\"4_008yb\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_pressed.gd\" id=\"4_t70fr\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action_mapping.gd\" id=\"5_xuekb\"]\n[ext_resource type=\"Resource\" uid=\"uid://3vqfs786vcsa\" path=\"res://guide_examples/remapping/mapping_contexts/keyboard_actions/switch_to_controller.tres\" id=\"6_lkk3b\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_mapping_context.gd\" id=\"6_rsvyd\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_any.gd\" id=\"7_b4hkn\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_rqm6k\"]\nscript = ExtResource(\"2_a3vaw\")\nkey = 4194305\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_yfr8v\"]\nscript = ExtResource(\"4_008yb\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_lju6e\"]\nscript = ExtResource(\"3_n037t\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_rqm6k\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_yfr8v\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_djru6\"]\nscript = ExtResource(\"5_xuekb\")\naction = ExtResource(\"1_j25bp\")\ninput_mappings = Array[ExtResource(\"3_n037t\")]([SubResource(\"Resource_lju6e\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_mpqi8\"]\nscript = ExtResource(\"7_b4hkn\")\nmouse_buttons = false\nmouse_movement = false\nminimum_mouse_movement_distance = 1.0\njoy_buttons = true\njoy_axes = false\nminimum_joy_axis_actuation_strength = 0.2\nkeyboard = false\ntouch = false\nmouse = false\njoy = true\n\n[sub_resource type=\"Resource\" id=\"Resource_4apa3\"]\nscript = ExtResource(\"4_t70fr\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_0hgmj\"]\nscript = ExtResource(\"3_n037t\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_mpqi8\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_4apa3\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_2neno\"]\nscript = ExtResource(\"5_xuekb\")\naction = ExtResource(\"6_lkk3b\")\ninput_mappings = Array[ExtResource(\"3_n037t\")]([SubResource(\"Resource_0hgmj\")])\n\n[resource]\nscript = ExtResource(\"6_rsvyd\")\ndisplay_name = \"\"\nmappings = Array[ExtResource(\"5_xuekb\")]([SubResource(\"Resource_djru6\"), SubResource(\"Resource_2neno\")])\n"
  },
  {
    "path": "guide_examples/remapping/mapping_contexts/controller.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMappingContext\" load_steps=30 format=3 uid=\"uid://bexjevffjsh3i\"]\n\n[ext_resource type=\"Resource\" uid=\"uid://chhw5umkd1j2p\" path=\"res://guide_examples/remapping/mapping_contexts/shared_actions/player_movement_2d.tres\" id=\"1_78yyx\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_mapping_context.gd\" id=\"1_l4253\"]\n[ext_resource type=\"Resource\" uid=\"uid://bv2xv48haqh2b\" path=\"res://guide_examples/remapping/mapping_contexts/controller_actions/player_movement_stick_deadzone.tres\" id=\"3_xdxov\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_input_mapping.gd\" id=\"4_m37gr\"]\n[ext_resource type=\"Resource\" uid=\"uid://qikyr1rgxw2l\" path=\"res://guide_examples/remapping/mapping_contexts/controller_actions/player_movement_stick_invert.tres\" id=\"4_qbgwt\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action_mapping.gd\" id=\"5_167oa\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_joy_axis_2d.gd\" id=\"6_y041f\"]\n[ext_resource type=\"Resource\" uid=\"uid://ce3ytxn2tcxxe\" path=\"res://guide_examples/remapping/mapping_contexts/controller_actions/switch_to_keyboard.tres\" id=\"8_4d4ov\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_any.gd\" id=\"9_0d1uf\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_released.gd\" id=\"9_eexjp\"]\n[ext_resource type=\"Resource\" uid=\"uid://c65tsmp268vdq\" path=\"res://guide_examples/remapping/mapping_contexts/shared_actions/open_menu.tres\" id=\"9_l4c4j\"]\n[ext_resource type=\"Resource\" uid=\"uid://b1iaet1m2gi2e\" path=\"res://guide_examples/remapping/mapping_contexts/shared_actions/fire.tres\" id=\"9_tkveh\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_joy_button.gd\" id=\"10_a30o8\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_pressed.gd\" id=\"11_avbpy\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_nr3w6\"]\nscript = ExtResource(\"6_y041f\")\nx = 0\ny = 1\njoy_index = -1\n\n[sub_resource type=\"Resource\" id=\"Resource_gpn8l\"]\nscript = ExtResource(\"4_m37gr\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_nr3w6\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([ExtResource(\"3_xdxov\"), ExtResource(\"4_qbgwt\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_6h1my\"]\nscript = ExtResource(\"5_167oa\")\naction = ExtResource(\"1_78yyx\")\ninput_mappings = Array[ExtResource(\"4_m37gr\")]([SubResource(\"Resource_gpn8l\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_p73kx\"]\nscript = ExtResource(\"10_a30o8\")\nbutton = 6\njoy_index = -1\n\n[sub_resource type=\"Resource\" id=\"Resource_uov21\"]\nscript = ExtResource(\"9_eexjp\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_f4p62\"]\nscript = ExtResource(\"4_m37gr\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_p73kx\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_uov21\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_wa31m\"]\nscript = ExtResource(\"5_167oa\")\naction = ExtResource(\"9_l4c4j\")\ninput_mappings = Array[ExtResource(\"4_m37gr\")]([SubResource(\"Resource_f4p62\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_iwnk1\"]\nscript = ExtResource(\"9_0d1uf\")\nmouse_buttons = false\nmouse_movement = false\nminimum_mouse_movement_distance = 1.0\njoy_buttons = false\njoy_axes = false\nminimum_joy_axis_actuation_strength = 0.2\nkeyboard = true\ntouch = false\nmouse = false\njoy = false\n\n[sub_resource type=\"Resource\" id=\"Resource_jwpon\"]\nscript = ExtResource(\"11_avbpy\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_limxc\"]\nscript = ExtResource(\"4_m37gr\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_iwnk1\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_jwpon\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_rtwk8\"]\nscript = ExtResource(\"5_167oa\")\naction = ExtResource(\"8_4d4ov\")\ninput_mappings = Array[ExtResource(\"4_m37gr\")]([SubResource(\"Resource_limxc\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_tsvy3\"]\nscript = ExtResource(\"10_a30o8\")\nbutton = 0\njoy_index = -1\n\n[sub_resource type=\"Resource\" id=\"Resource_qvmci\"]\nscript = ExtResource(\"11_avbpy\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_hf22v\"]\nscript = ExtResource(\"4_m37gr\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_tsvy3\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_qvmci\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_t70e5\"]\nscript = ExtResource(\"5_167oa\")\naction = ExtResource(\"9_tkveh\")\ninput_mappings = Array[ExtResource(\"4_m37gr\")]([SubResource(\"Resource_hf22v\")])\n\n[resource]\nscript = ExtResource(\"1_l4253\")\ndisplay_name = \"\"\nmappings = Array[ExtResource(\"5_167oa\")]([SubResource(\"Resource_6h1my\"), SubResource(\"Resource_wa31m\"), SubResource(\"Resource_rtwk8\"), SubResource(\"Resource_t70e5\")])\n"
  },
  {
    "path": "guide_examples/remapping/mapping_contexts/controller_actions/next_tab.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://dg1or0do0s1ad\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_432ak\"]\n\n[resource]\nscript = ExtResource(\"1_432ak\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/remapping/mapping_contexts/controller_actions/player_movement_stick_deadzone.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEModifierDeadzone\" load_steps=2 format=3 uid=\"uid://bv2xv48haqh2b\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_deadzone.gd\" id=\"1_w62m8\"]\n\n[resource]\nscript = ExtResource(\"1_w62m8\")\nlower_threshold = 0.2\nupper_threshold = 1.0\n"
  },
  {
    "path": "guide_examples/remapping/mapping_contexts/controller_actions/player_movement_stick_invert.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEModifierNegate\" load_steps=2 format=3 uid=\"uid://qikyr1rgxw2l\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_negate.gd\" id=\"1_geiqh\"]\n\n[resource]\nscript = ExtResource(\"1_geiqh\")\nx = false\ny = false\nz = false\n"
  },
  {
    "path": "guide_examples/remapping/mapping_contexts/controller_actions/previous_tab.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://cfrx54l1vmjey\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_mbx72\"]\n\n[resource]\nscript = ExtResource(\"1_mbx72\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/remapping/mapping_contexts/controller_actions/switch_to_keyboard.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://ce3ytxn2tcxxe\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_gwq63\"]\n\n[resource]\nscript = ExtResource(\"1_gwq63\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/remapping/mapping_contexts/controller_actions/ui_accept.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://bohjda85owgnc\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_laec0\"]\n\n[resource]\nscript = ExtResource(\"1_laec0\")\nname = &\"ui_accept\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = true\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/remapping/mapping_contexts/keyboard.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMappingContext\" load_steps=38 format=3 uid=\"uid://cu0dhstc00cj5\"]\n\n[ext_resource type=\"Resource\" uid=\"uid://chhw5umkd1j2p\" path=\"res://guide_examples/remapping/mapping_contexts/shared_actions/player_movement_2d.tres\" id=\"1_uy1j0\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_negate.gd\" id=\"4_5oaiq\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_input_mapping.gd\" id=\"5_d2nln\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_input_swizzle.gd\" id=\"8_vp516\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action_mapping.gd\" id=\"10_xpxg7\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_key.gd\" id=\"12_msrvd\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_released.gd\" id=\"13_11jxi\"]\n[ext_resource type=\"Resource\" uid=\"uid://c65tsmp268vdq\" path=\"res://guide_examples/remapping/mapping_contexts/shared_actions/open_menu.tres\" id=\"13_m7li8\"]\n[ext_resource type=\"Resource\" uid=\"uid://3vqfs786vcsa\" path=\"res://guide_examples/remapping/mapping_contexts/keyboard_actions/switch_to_controller.tres\" id=\"14_57mry\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_pressed.gd\" id=\"14_gcar0\"]\n[ext_resource type=\"Resource\" uid=\"uid://b1iaet1m2gi2e\" path=\"res://guide_examples/remapping/mapping_contexts/shared_actions/fire.tres\" id=\"15_7eor3\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_any.gd\" id=\"15_51tw7\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_mapping_context.gd\" id=\"15_qjsfb\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_d5vxv\"]\nscript = ExtResource(\"12_msrvd\")\nkey = 87\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_d5crb\"]\nscript = ExtResource(\"8_vp516\")\norder = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_cwfnu\"]\nscript = ExtResource(\"4_5oaiq\")\nx = true\ny = true\nz = true\n\n[sub_resource type=\"Resource\" id=\"Resource_u7h55\"]\nscript = ExtResource(\"5_d2nln\")\noverride_action_settings = true\nis_remappable = true\ndisplay_name = \"Up\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_d5vxv\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_d5crb\"), SubResource(\"Resource_cwfnu\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_syl0m\"]\nscript = ExtResource(\"12_msrvd\")\nkey = 83\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_8rcpo\"]\nscript = ExtResource(\"8_vp516\")\norder = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_te6bu\"]\nscript = ExtResource(\"5_d2nln\")\noverride_action_settings = true\nis_remappable = true\ndisplay_name = \"Down\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_syl0m\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_8rcpo\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_erj62\"]\nscript = ExtResource(\"12_msrvd\")\nkey = 65\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_84clu\"]\nscript = ExtResource(\"4_5oaiq\")\nx = true\ny = true\nz = true\n\n[sub_resource type=\"Resource\" id=\"Resource_ho2kd\"]\nscript = ExtResource(\"5_d2nln\")\noverride_action_settings = true\nis_remappable = true\ndisplay_name = \"Left\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_erj62\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_84clu\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_ybtim\"]\nscript = ExtResource(\"12_msrvd\")\nkey = 68\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_bnk54\"]\nscript = ExtResource(\"5_d2nln\")\noverride_action_settings = true\nis_remappable = true\ndisplay_name = \"Right\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_ybtim\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_358t4\"]\nscript = ExtResource(\"10_xpxg7\")\naction = ExtResource(\"1_uy1j0\")\ninput_mappings = Array[ExtResource(\"5_d2nln\")]([SubResource(\"Resource_u7h55\"), SubResource(\"Resource_te6bu\"), SubResource(\"Resource_ho2kd\"), SubResource(\"Resource_bnk54\")])\nmetadata/_guide_input_mappings_collapsed = false\n\n[sub_resource type=\"Resource\" id=\"Resource_la24a\"]\nscript = ExtResource(\"12_msrvd\")\nkey = 4194305\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_dbmrn\"]\nscript = ExtResource(\"13_11jxi\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_wr8lq\"]\nscript = ExtResource(\"5_d2nln\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_la24a\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_dbmrn\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_iwmfr\"]\nscript = ExtResource(\"10_xpxg7\")\naction = ExtResource(\"13_m7li8\")\ninput_mappings = Array[ExtResource(\"5_d2nln\")]([SubResource(\"Resource_wr8lq\")])\nmetadata/_guide_input_mappings_collapsed = false\n\n[sub_resource type=\"Resource\" id=\"Resource_ke2lc\"]\nscript = ExtResource(\"15_51tw7\")\nmouse_buttons = false\nmouse_movement = false\nminimum_mouse_movement_distance = 1.0\njoy_buttons = true\njoy_axes = false\nminimum_joy_axis_actuation_strength = 0.2\nkeyboard = false\ntouch = false\nmouse = false\njoy = true\n\n[sub_resource type=\"Resource\" id=\"Resource_oh8td\"]\nscript = ExtResource(\"5_d2nln\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_ke2lc\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_unkjf\"]\nscript = ExtResource(\"10_xpxg7\")\naction = ExtResource(\"14_57mry\")\ninput_mappings = Array[ExtResource(\"5_d2nln\")]([SubResource(\"Resource_oh8td\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_h3p6j\"]\nscript = ExtResource(\"12_msrvd\")\nkey = 32\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_saax4\"]\nscript = ExtResource(\"14_gcar0\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_nm3w5\"]\nscript = ExtResource(\"5_d2nln\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_h3p6j\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_saax4\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_cv74y\"]\nscript = ExtResource(\"10_xpxg7\")\naction = ExtResource(\"15_7eor3\")\ninput_mappings = Array[ExtResource(\"5_d2nln\")]([SubResource(\"Resource_nm3w5\")])\nmetadata/_guide_input_mappings_collapsed = false\n\n[resource]\nscript = ExtResource(\"15_qjsfb\")\ndisplay_name = \"Keyboard and Mouse\"\nmappings = Array[ExtResource(\"10_xpxg7\")]([SubResource(\"Resource_358t4\"), SubResource(\"Resource_iwmfr\"), SubResource(\"Resource_unkjf\"), SubResource(\"Resource_cv74y\")])\n"
  },
  {
    "path": "guide_examples/remapping/mapping_contexts/keyboard_actions/switch_to_controller.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://3vqfs786vcsa\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_ahfs1\"]\n\n[resource]\nscript = ExtResource(\"1_ahfs1\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/remapping/mapping_contexts/shared_actions/close_menu.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://bcum2m26we6ct\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_q87d1\"]\n\n[resource]\nscript = ExtResource(\"1_q87d1\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/remapping/mapping_contexts/shared_actions/fire.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://b1iaet1m2gi2e\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_d1iw0\"]\n\n[resource]\nscript = ExtResource(\"1_d1iw0\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = true\ndisplay_name = \"Fire\"\ndisplay_category = \"Player Actions\"\n"
  },
  {
    "path": "guide_examples/remapping/mapping_contexts/shared_actions/open_menu.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://c65tsmp268vdq\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_rdx5a\"]\n\n[resource]\nscript = ExtResource(\"1_rdx5a\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/remapping/mapping_contexts/shared_actions/player_movement_2d.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://chhw5umkd1j2p\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_ecpj4\"]\n\n[resource]\nscript = ExtResource(\"1_ecpj4\")\nname = &\"\"\naction_value_type = 2\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = true\ndisplay_name = \"Move\"\ndisplay_category = \"Player Actions\"\n"
  },
  {
    "path": "guide_examples/remapping/player.gd",
    "content": "## This is  the player script. Note how it has no clue about controllers, axis inversion\n## etc. This is all handled by GUIDE and the remapping dialog.\nextends Node2D\n\n@export var speed:float = 300\n@export var move_action:GUIDEAction\n@export var fire_action:GUIDEAction\n\n@export var fireball_scene:PackedScene\n\nfunc _ready() -> void:\n\tfire_action.triggered.connect(_shoot_fireball)\n\n\nfunc _process(delta:float) -> void:\n\tvar vector: Vector2 = move_action.value_axis_2d\n\t\n\t# Circular length limiting\n\tvar length: float = vector.length();\n\tvar modified_vector: Vector2 = Vector2.ZERO\n\tif length <= 0:\n\t\tmodified_vector = Vector2.ZERO\n\telif length > 1.0:\n\t\tmodified_vector = vector / length;\n\telse:\n\t\tmodified_vector = vector * (inverse_lerp(0.0, 1.0, length) / length)\n\t\n\tposition += modified_vector * speed * delta\n\n\nfunc _shoot_fireball() -> void:\n\tvar fireball:Node = fireball_scene.instantiate()\n\tfireball.direction = Vector2.UP\n\tget_parent().add_child(fireball)\n\t\n\tfireball.global_transform = global_transform\n"
  },
  {
    "path": "guide_examples/remapping/player.gd.uid",
    "content": "uid://bhn2nskt2cgym\n"
  },
  {
    "path": "guide_examples/remapping/remapping.gd",
    "content": "## This is the main game controller. It enables a control scheme at the start and is\n## responsible for controlling the remapping dialog.\nextends Node\n\nconst Utils = preload(\"utils.gd\")\n\n@export_group(\"Context & Modifiers\")\n@export var keyboard:GUIDEMappingContext\n@export var controller:GUIDEMappingContext\n@export var controller_axis_invert_modifier:GUIDEModifierNegate\n@export var controller_axis_deadzone:GUIDEModifierDeadzone\n\n@export_group(\"Actions\")\n@export var switch_to_keyboard:GUIDEAction\n@export var switch_to_controller:GUIDEAction\n@export var open_menu:GUIDEAction\n\n\n@onready var _remapping_dialog:Control = %RemappingDialog\n\nfunc _ready() -> void:\n\t# React when the open menu action is triggered.\n\topen_menu.triggered.connect(_open_menu)\n\t\n\t# and switching to controller / keyboard ... \n\tswitch_to_controller.triggered.connect(_switch.bind(controller))\n\tswitch_to_keyboard.triggered.connect(_switch.bind(keyboard))\n\t\n\t# Also listen to when the remapping dialog closes and re-apply the changed\n\t# mapping config\n\t_remapping_dialog.closed.connect(_load_remapping_config)\n\t\n\t# Start with the keyboard scheme\n\tGUIDE.enable_mapping_context(keyboard)\n\t\n\t# finally enable all controls with the last saved remapping configuration\n\t_load_remapping_config(Utils.load_remapping_config())\n\t\n\t\nfunc _open_menu() -> void:\n\t# and show the remapping dialog\n\t_remapping_dialog.open()\n\t\n\t\nfunc _load_remapping_config(config:GUIDERemappingConfig) -> void:\n\tGUIDE.set_remapping_config(config)\n\t\n\t# also apply changes to our modifiers\n\tcontroller_axis_invert_modifier.x = config.custom_data.get(Utils.CUSTOM_DATA_INVERT_HORIZONTAL, false)\n\tcontroller_axis_invert_modifier.y = config.custom_data.get(Utils.CUSTOM_DATA_INVERT_VERTICAL, false)\n\t\n\tcontroller_axis_deadzone.lower_threshold = config.custom_data.get(Utils.CUSTOM_DATA_MOVEMENT_DEADZONE, 0.2)\n\n\nfunc _switch(context:GUIDEMappingContext) -> void:\n\t# ignore while remapping is active, remapping will take care of it\n\tif _remapping_dialog.visible:\n\t\treturn\n\t\t\n\tGUIDE.enable_mapping_context(context, true)\n"
  },
  {
    "path": "guide_examples/remapping/remapping.gd.uid",
    "content": "uid://ba5134rplb4y\n"
  },
  {
    "path": "guide_examples/remapping/remapping.tscn",
    "content": "[gd_scene load_steps=18 format=3 uid=\"uid://gjweqc0stfqh\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/remapping/remapping.gd\" id=\"1_3d1tp\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://byjxtsekdl8t2\" path=\"res://guide_examples/shared/godot_logo.svg\" id=\"1_gkmxq\"]\n[ext_resource type=\"Resource\" uid=\"uid://qikyr1rgxw2l\" path=\"res://guide_examples/remapping/mapping_contexts/controller_actions/player_movement_stick_invert.tres\" id=\"2_3dav4\"]\n[ext_resource type=\"Resource\" uid=\"uid://cu0dhstc00cj5\" path=\"res://guide_examples/remapping/mapping_contexts/keyboard.tres\" id=\"2_issuo\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/remapping/player.gd\" id=\"2_n3drv\"]\n[ext_resource type=\"Resource\" uid=\"uid://bexjevffjsh3i\" path=\"res://guide_examples/remapping/mapping_contexts/controller.tres\" id=\"3_apwxt\"]\n[ext_resource type=\"Resource\" uid=\"uid://c65tsmp268vdq\" path=\"res://guide_examples/remapping/mapping_contexts/shared_actions/open_menu.tres\" id=\"3_g1dlj\"]\n[ext_resource type=\"Resource\" uid=\"uid://chhw5umkd1j2p\" path=\"res://guide_examples/remapping/mapping_contexts/shared_actions/player_movement_2d.tres\" id=\"4_07jn1\"]\n[ext_resource type=\"Resource\" uid=\"uid://bv2xv48haqh2b\" path=\"res://guide_examples/remapping/mapping_contexts/controller_actions/player_movement_stick_deadzone.tres\" id=\"5_0d7qx\"]\n[ext_resource type=\"Resource\" uid=\"uid://ce3ytxn2tcxxe\" path=\"res://guide_examples/remapping/mapping_contexts/controller_actions/switch_to_keyboard.tres\" id=\"5_aqwgr\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://dkr80d2pi0d41\" path=\"res://addons/guide/debugger/guide_debugger.tscn\" id=\"5_kdgir\"]\n[ext_resource type=\"Resource\" uid=\"uid://b1iaet1m2gi2e\" path=\"res://guide_examples/remapping/mapping_contexts/shared_actions/fire.tres\" id=\"6_esnhm\"]\n[ext_resource type=\"Resource\" uid=\"uid://3vqfs786vcsa\" path=\"res://guide_examples/remapping/mapping_contexts/keyboard_actions/switch_to_controller.tres\" id=\"6_jncg0\"]\n[ext_resource type=\"Theme\" uid=\"uid://dot0gi1yoqmrl\" path=\"res://guide_examples/shared/ui_theme.tres\" id=\"7_8t2l7\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://bq0w7uaotgfct\" path=\"res://guide_examples/remapping/ui/remapping_dialog.tscn\" id=\"7_g0vxv\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://c36cnvgv2ur60\" path=\"res://guide_examples/shared/fireball/fireball.tscn\" id=\"7_w2y2e\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/shared/instructions_label.gd\" id=\"14_e0a18\"]\n\n[node name=\"Remapping\" type=\"Node\"]\nscript = ExtResource(\"1_3d1tp\")\nkeyboard = ExtResource(\"2_issuo\")\ncontroller = ExtResource(\"3_apwxt\")\ncontroller_axis_invert_modifier = ExtResource(\"2_3dav4\")\ncontroller_axis_deadzone = ExtResource(\"5_0d7qx\")\nswitch_to_keyboard = ExtResource(\"5_aqwgr\")\nswitch_to_controller = ExtResource(\"6_jncg0\")\nopen_menu = ExtResource(\"3_g1dlj\")\n\n[node name=\"Player\" type=\"Sprite2D\" parent=\".\"]\nposition = Vector2(546, 317)\ntexture = ExtResource(\"1_gkmxq\")\nscript = ExtResource(\"2_n3drv\")\nmove_action = ExtResource(\"4_07jn1\")\nfire_action = ExtResource(\"6_esnhm\")\nfireball_scene = ExtResource(\"7_w2y2e\")\n\n[node name=\"HUD Layer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"Label\" type=\"RichTextLabel\" parent=\"HUD Layer\"]\nanchors_preset = 1\nanchor_left = 1.0\nanchor_right = 1.0\noffset_left = -16.0\noffset_top = 12.0\noffset_right = -15.0\noffset_bottom = 12.0\ngrow_horizontal = 0\ntheme = ExtResource(\"7_8t2l7\")\nbbcode_enabled = true\nfit_content = true\nautowrap_mode = 0\nscript = ExtResource(\"14_e0a18\")\ninstructions_text = \"Use %s to move.\nPress %s to fire a fireball.\nPress %s to change input mappings.\"\nactions = Array[Resource(\"res://addons/guide/guide_action.gd\")]([ExtResource(\"4_07jn1\"), ExtResource(\"6_esnhm\"), ExtResource(\"3_g1dlj\")])\n\n[node name=\"UI Layer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"RemappingDialog\" parent=\"UI Layer\" instance=ExtResource(\"7_g0vxv\")]\nunique_name_in_owner = true\nvisible = false\n\n[node name=\"DebugLayer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"GuideDebugger\" parent=\"DebugLayer\" instance=ExtResource(\"5_kdgir\")]\ntheme = ExtResource(\"7_8t2l7\")\n"
  },
  {
    "path": "guide_examples/remapping/ui/binding_row.gd",
    "content": "extends HBoxContainer\n\n\nsignal rebind(item:GUIDERemapper.ConfigItem)\n\n@onready var _action_name:Button = %ActionName\n@onready var _action_binding:RichTextLabel = %ActionBinding\n\nvar _formatter:GUIDEInputFormatter = GUIDEInputFormatter.new(48)\nvar _item:GUIDERemapper.ConfigItem\n\nfunc initialize(item:GUIDERemapper.ConfigItem, input:GUIDEInput) -> void:\n\t_item = item\n\t_action_name.text = item.display_name\n\t_item.changed.connect(_show_input)\n\t_show_input(input)\n\t\n\t\nfunc _on_action_name_pressed() -> void:\n\tif _item != null:\n\t\trebind.emit(_item)\n\n\nfunc _show_input(input:GUIDEInput) -> void:\n\tif input != null:\n\t\tvar text := await _formatter.input_as_richtext_async(input)\n\t\t_action_binding.parse_bbcode(text)\t\n\telse:\n\t\t_action_binding.parse_bbcode(\"<not bound>\")\n"
  },
  {
    "path": "guide_examples/remapping/ui/binding_row.gd.uid",
    "content": "uid://cfukq2iogf0k4\n"
  },
  {
    "path": "guide_examples/remapping/ui/binding_row.tscn",
    "content": "[gd_scene load_steps=2 format=3 uid=\"uid://bme1y0ikthda7\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/remapping/ui/binding_row.gd\" id=\"1_mc50g\"]\n\n[node name=\"BindingRow\" type=\"HBoxContainer\"]\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\ntheme_override_constants/separation = 10\nscript = ExtResource(\"1_mc50g\")\n\n[node name=\"ActionName\" type=\"Button\" parent=\".\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 10\ntext = \"Lorem ipsum\"\nflat = true\n\n[node name=\"ActionBinding\" type=\"RichTextLabel\" parent=\".\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 2\nsize_flags_vertical = 4\nbbcode_enabled = true\nfit_content = true\nscroll_active = false\nautowrap_mode = 0\nshortcut_keys_enabled = false\n\n[connection signal=\"pressed\" from=\"ActionName\" to=\".\" method=\"_on_action_name_pressed\"]\n"
  },
  {
    "path": "guide_examples/remapping/ui/binding_section.gd",
    "content": "@tool\nextends MarginContainer\n\n@onready var _label:Label = %Label\n\n@export var text:String:\n\tset(value):\n\t\ttext = value\n\t\t_refresh()\n\t\t\n\t\t\nfunc _ready() -> void:\n\t_refresh()\n\t\t\nfunc _refresh() -> void:\n\tif _label != null:\n\t\t_label.text = text\n\t\n"
  },
  {
    "path": "guide_examples/remapping/ui/binding_section.gd.uid",
    "content": "uid://dt4gdj0rt5mnw\n"
  },
  {
    "path": "guide_examples/remapping/ui/binding_section.tscn",
    "content": "[gd_scene load_steps=3 format=3 uid=\"uid://cj1h0wxamje4s\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/remapping/ui/binding_section.gd\" id=\"1_hoxsv\"]\n\n[sub_resource type=\"StyleBoxFlat\" id=\"StyleBoxFlat_h8l7u\"]\nbg_color = Color(0.355314, 0.355314, 0.355313, 1)\ncorner_radius_top_left = 10\ncorner_radius_top_right = 10\ncorner_radius_bottom_right = 10\ncorner_radius_bottom_left = 10\n\n[node name=\"MarginContainer\" type=\"MarginContainer\"]\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\ntheme_override_constants/margin_top = 10\ntheme_override_constants/margin_bottom = 10\nscript = ExtResource(\"1_hoxsv\")\n\n[node name=\"Panel\" type=\"Panel\" parent=\".\"]\nlayout_mode = 2\ntheme_override_styles/panel = SubResource(\"StyleBoxFlat_h8l7u\")\n\n[node name=\"BindingSection\" type=\"MarginContainer\" parent=\".\"]\nlayout_mode = 2\ntheme_override_constants/margin_left = 5\ntheme_override_constants/margin_top = 5\ntheme_override_constants/margin_right = 5\ntheme_override_constants/margin_bottom = 5\n\n[node name=\"Label\" type=\"Label\" parent=\"BindingSection\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntheme_override_font_sizes/font_size = 22\n"
  },
  {
    "path": "guide_examples/remapping/ui/remapping_dialog.gd",
    "content": "## The remapping dialog. \nextends MarginContainer\n\nsignal closed(applied_config:GUIDERemappingConfig)\n\nconst Utils = preload(\"../utils.gd\")\n\n# Input\n@export var keyboard_context:GUIDEMappingContext\n@export var controller_context:GUIDEMappingContext\n@export var binding_keyboard_context:GUIDEMappingContext\n@export var binding_controller_context:GUIDEMappingContext\n@export var close_dialog:GUIDEAction\n@export var switch_to_controller:GUIDEAction\n@export var switch_to_keyboard:GUIDEAction\n@export var previous_tab:GUIDEAction\n@export var next_tab:GUIDEAction\n\n# UI \n@export var binding_row_scene:PackedScene\n@export var binding_section_scene:PackedScene\n\n@onready var _keyboard_bindings:Container = %KeyboardBindings\n@onready var _controller_bindings:Container = %ControllerBindings\n@onready var _press_prompt:Control = %PressPrompt\n@onready var _controller_invert_horizontal:CheckBox = %ControllerInvertHorizontal\n@onready var _controller_invert_vertical:CheckBox = %ControllerInvertVertical\n@onready var _deadzone_h_slider:HSlider = %DeadzoneHSlider\n@onready var _deadzone_spin_box:SpinBox = %DeadzoneSpinBox\n@onready var _tab_container:TabContainer = %TabContainer\n\n## The input detector for detecting new input\n@onready var _input_detector:GUIDEInputDetector = %GUIDEInputDetector\n\n## The remapper, helps us quickly remap inputs.\nvar _remapper:GUIDERemapper = GUIDERemapper.new()\n\n## The config we're currently working on\nvar _remapping_config:GUIDERemappingConfig\n\n## The last control that was focused when we started input detection.\n## Used to restore focus afterwards.\nvar _focused_control:Control = null\n\nfunc _ready() -> void:\n\t# connect the actions that the remapping dialog uses\n\tclose_dialog.triggered.connect(_on_close_dialog)\n\tswitch_to_controller.triggered.connect(_switch.bind(binding_controller_context))\n\tswitch_to_keyboard.triggered.connect(_switch.bind(binding_keyboard_context))\n\tprevious_tab.triggered.connect(_switch_tab.bind(-1))\n\tnext_tab.triggered.connect(_switch_tab.bind(1))\n\t\n\nfunc open() -> void:\n\t# switch the tab to the scheme that is currently enabled\n\t# to make life a bit easier for the player, and also\n\t# enable the correct mapping context for the binding dialog\n\tif GUIDE.is_mapping_context_enabled(controller_context):\n\t\t_tab_container.current_tab = 1\n\t\tGUIDE.enable_mapping_context(binding_controller_context, true)\n\telse:\n\t\t_tab_container.current_tab = 0\n\t\tGUIDE.enable_mapping_context(binding_keyboard_context, true)\n\t\t\n\t# todo provide specific actions for the tab bar controller\n\t_tab_container.get_tab_bar().grab_focus()\n\t\n\t# Open the user's last edited remapping config, if it exists\n\t_remapping_config = Utils.load_remapping_config()\n\t\n\t# And initialize the remapper\n\t_remapper.initialize([keyboard_context, controller_context], _remapping_config)\n\t\n\t_clear(_keyboard_bindings)\n\t_clear(_controller_bindings)\n\t\n\t# fill the keyboard section\n\t_fill_remappable_items(keyboard_context, _keyboard_bindings)\n\t\n\t# fill the controller section\n\t_fill_remappable_items(controller_context, _controller_bindings)\n\t\n\t_controller_invert_horizontal.button_pressed = _remapper.get_custom_data(\"invert_horizontal\", false)\n\t_controller_invert_vertical.button_pressed = _remapper.get_custom_data(\"invert_vertical\", false)\n\t\n\tvar deadzone:float = _remapper.get_custom_data(Utils.CUSTOM_DATA_MOVEMENT_DEADZONE, 0.2)\n\t_deadzone_h_slider.value = deadzone\n\t_deadzone_spin_box.value = deadzone\n\t\n\tvisible = true\n\t\n\t\n## Fills remappable items and sub-sections into the given container\t\nfunc _fill_remappable_items(context:GUIDEMappingContext, root:Container) -> void:\n\tvar items := _remapper.get_remappable_items(context)\n\tvar section_name := \"\"\n\tfor item in items:\n\t\tif item.display_category != section_name:\n\t\t\tsection_name = item.display_category\n\t\t\tvar section:Node = binding_section_scene.instantiate()\n\t\t\troot.add_child(section)\n\t\t\tsection.text = section_name\n\t\t\t\n\t\tvar instance:Node = binding_row_scene.instantiate()\n\t\troot.add_child(instance)\n\t\t\n\t\t# Show the current binding.\n\t\tinstance.initialize(item, _remapper.get_bound_input_or_null(item))\n\t\tinstance.rebind.connect(_rebind_item)\n\n\n\nfunc _rebind_item(item:GUIDERemapper.ConfigItem) -> void:\n\t_focused_control = get_viewport().gui_get_focus_owner()\n\t_focused_control.release_focus()\n\t\n\t_press_prompt.visible = true\n\n\t# Limit the devices that we can detect based on which\n\t# mapping context we're currently working on. So \n\t# for keyboard only keys can be bound and for controller\n\t# only controller buttons can be bound.\n\tvar device := GUIDEInputDetector.DeviceType.KEYBOARD\n\tif item.context == controller_context:\n\t\tdevice = GUIDEInputDetector.DeviceType.JOY\n\n\t# detect a new input\n\t_input_detector.detect(item.value_type, [device])\n\tvar input:GUIDEInput = await _input_detector.input_detected\n\n\t_press_prompt.visible = false\n\n\t_focused_control.grab_focus()\n\n\t# check if the detection was aborted.\n\tif input == null:\n\t\treturn\n\n\t# check for collisions \n\tvar collisions := _remapper.get_input_collisions(item, input)\n\t\t\n\t# if any collision is from a non-bindable mapping, we cannot use this input\n\tif collisions.any(func(it:GUIDERemapper.ConfigItem) -> bool: return not it.is_remappable):\n\t\treturn\n\t\t\n\t# unbind the colliding entries.\n\tfor collision in collisions:\n\t\t_remapper.set_bound_input(collision, null)\n\t\t\n\t# now bind the new input\n\t_remapper.set_bound_input(item, input)\n\t\t\t\n\n\nfunc _clear(root:Container) -> void:\n\tfor child in root.get_children():\n\t\troot.remove_child(child)\n\t\tchild.queue_free()\n\t\t\n\t\t\nfunc _on_abort_detection() -> void:\n\t_input_detector.abort_detection()\n\nfunc _on_close_dialog() -> void:\n\tif _input_detector.is_detecting:\n\t\treturn\n\t# same as pressing return to game\t\n\t_on_return_to_game_pressed()\n\nfunc _on_controller_invert_horizontal_toggled(toggled_on:bool) -> void:\n\t_remapper.set_custom_data(Utils.CUSTOM_DATA_INVERT_HORIZONTAL, toggled_on)\n\n\nfunc _on_controller_invert_vertical_toggled(toggled_on:bool) -> void:\n\t_remapper.set_custom_data(Utils.CUSTOM_DATA_INVERT_VERTICAL, toggled_on)\n\nfunc _on_deadzone_h_slider_value_changed(value:float) -> void:\n\t_remapper.set_custom_data(Utils.CUSTOM_DATA_MOVEMENT_DEADZONE, value)\n\t_deadzone_spin_box.value = value\n\nfunc _on_deadzone_spin_box_value_changed(value:float) -> void:\n\t_remapper.set_custom_data(Utils.CUSTOM_DATA_MOVEMENT_DEADZONE, value)\n\t_deadzone_h_slider.value = value\n\n\nfunc _on_return_to_game_pressed() -> void:\n\t# get the modified config\n\tvar final_config := _remapper.get_mapping_config()\n\t# store it\n\tUtils.save_remapping_config(final_config)\n\n\t# restore main mapping context based on what is currently active\n\tif GUIDE.is_mapping_context_enabled(binding_keyboard_context):\n\t\tGUIDE.enable_mapping_context(keyboard_context, true)\n\telse:\n\t\tGUIDE.enable_mapping_context(controller_context, true)\n\n\t# and close the dialog\n\tvisible = false\n\tclosed.emit(final_config)\n\t\n\t\nfunc _switch_tab(index:int) -> void:\n\t_tab_container.current_tab = posmod(_tab_container.current_tab + index, 2)\t\n\nfunc _switch(context:GUIDEMappingContext) -> void:\n\t# only do this when the dialog is visible\n\tif not visible:\n\t\treturn\n\t\t\n\tGUIDE.enable_mapping_context(context, true)\n"
  },
  {
    "path": "guide_examples/remapping/ui/remapping_dialog.gd.uid",
    "content": "uid://5crxnd8ysf6\n"
  },
  {
    "path": "guide_examples/remapping/ui/remapping_dialog.tscn",
    "content": "[gd_scene load_steps=21 format=3 uid=\"uid://bq0w7uaotgfct\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/remapping/ui/remapping_dialog.gd\" id=\"1_6hgqj\"]\n[ext_resource type=\"Theme\" uid=\"uid://dot0gi1yoqmrl\" path=\"res://guide_examples/shared/ui_theme.tres\" id=\"1_uhsj0\"]\n[ext_resource type=\"Resource\" uid=\"uid://cu0dhstc00cj5\" path=\"res://guide_examples/remapping/mapping_contexts/keyboard.tres\" id=\"2_cgour\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/remapping/guide_input_detector.gd\" id=\"3_o0nvn\"]\n[ext_resource type=\"Resource\" uid=\"uid://bexjevffjsh3i\" path=\"res://guide_examples/remapping/mapping_contexts/controller.tres\" id=\"3_tgkdx\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://bme1y0ikthda7\" path=\"res://guide_examples/remapping/ui/binding_row.tscn\" id=\"4_iojgu\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://cj1h0wxamje4s\" path=\"res://guide_examples/remapping/ui/binding_section.tscn\" id=\"5_8v80s\"]\n[ext_resource type=\"Resource\" uid=\"uid://bqd45wwsetlyg\" path=\"res://guide_examples/remapping/mapping_contexts/binding_keyboard.tres\" id=\"5_tw3jw\"]\n[ext_resource type=\"Resource\" uid=\"uid://dubuepcs1w17f\" path=\"res://guide_examples/remapping/mapping_contexts/binding_controller.tres\" id=\"6_gdrkn\"]\n[ext_resource type=\"Resource\" uid=\"uid://bcum2m26we6ct\" path=\"res://guide_examples/remapping/mapping_contexts/shared_actions/close_menu.tres\" id=\"7_djty7\"]\n[ext_resource type=\"Resource\" uid=\"uid://3vqfs786vcsa\" path=\"res://guide_examples/remapping/mapping_contexts/keyboard_actions/switch_to_controller.tres\" id=\"8_lj8gw\"]\n[ext_resource type=\"Resource\" uid=\"uid://ce3ytxn2tcxxe\" path=\"res://guide_examples/remapping/mapping_contexts/controller_actions/switch_to_keyboard.tres\" id=\"9_brmt1\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/shared/instructions_label.gd\" id=\"10_xsw70\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_key.gd\" id=\"11_e078a\"]\n[ext_resource type=\"Resource\" uid=\"uid://cfrx54l1vmjey\" path=\"res://guide_examples/remapping/mapping_contexts/controller_actions/previous_tab.tres\" id=\"11_uxwof\"]\n[ext_resource type=\"Resource\" uid=\"uid://dg1or0do0s1ad\" path=\"res://guide_examples/remapping/mapping_contexts/controller_actions/next_tab.tres\" id=\"12_byojv\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_joy_button.gd\" id=\"12_hftpv\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_f3bao\"]\nscript = ExtResource(\"11_e078a\")\nkey = 4194305\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = false\n\n[sub_resource type=\"Resource\" id=\"Resource_twrga\"]\nscript = ExtResource(\"12_hftpv\")\nbutton = 4\njoy_index = -1\n\n[sub_resource type=\"StyleBoxFlat\" id=\"StyleBoxFlat_bagfg\"]\nbg_color = Color(0.266575, 0.266575, 0.266575, 1)\n\n[node name=\"RemappingDialog\" type=\"MarginContainer\"]\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\ntheme = ExtResource(\"1_uhsj0\")\nscript = ExtResource(\"1_6hgqj\")\nkeyboard_context = ExtResource(\"2_cgour\")\ncontroller_context = ExtResource(\"3_tgkdx\")\nbinding_keyboard_context = ExtResource(\"5_tw3jw\")\nbinding_controller_context = ExtResource(\"6_gdrkn\")\nclose_dialog = ExtResource(\"7_djty7\")\nswitch_to_controller = ExtResource(\"8_lj8gw\")\nswitch_to_keyboard = ExtResource(\"9_brmt1\")\nprevious_tab = ExtResource(\"11_uxwof\")\nnext_tab = ExtResource(\"12_byojv\")\nbinding_row_scene = ExtResource(\"4_iojgu\")\nbinding_section_scene = ExtResource(\"5_8v80s\")\n\n[node name=\"Blocker\" type=\"ColorRect\" parent=\".\"]\nlayout_mode = 2\ncolor = Color(8.66354e-07, 0.331199, 0.634906, 0.352941)\n\n[node name=\"CenterContainer\" type=\"CenterContainer\" parent=\".\"]\nlayout_mode = 2\n\n[node name=\"PanelContainer\" type=\"PanelContainer\" parent=\"CenterContainer\"]\nlayout_mode = 2\n\n[node name=\"MarginContainer\" type=\"MarginContainer\" parent=\"CenterContainer/PanelContainer\"]\nlayout_mode = 2\ntheme_override_constants/margin_left = 20\ntheme_override_constants/margin_top = 20\ntheme_override_constants/margin_right = 20\ntheme_override_constants/margin_bottom = 20\n\n[node name=\"VBoxContainer\" type=\"VBoxContainer\" parent=\"CenterContainer/PanelContainer/MarginContainer\"]\ncustom_minimum_size = Vector2(800, 600)\nlayout_mode = 2\ntheme_override_constants/separation = 20\n\n[node name=\"Label\" type=\"Label\" parent=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer\"]\nlayout_mode = 2\ntheme_type_variation = &\"HeaderLarge\"\ntext = \"Input Rebinding\"\nhorizontal_alignment = 1\n\n[node name=\"MarginContainer\" type=\"MarginContainer\" parent=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer\"]\nlayout_mode = 2\nsize_flags_vertical = 3\n\n[node name=\"TabContainer\" type=\"TabContainer\" parent=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_vertical = 3\ntab_alignment = 1\ncurrent_tab = 1\nclip_tabs = false\n\n[node name=\"Keyboard\" type=\"PanelContainer\" parent=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer\"]\nvisible = false\nlayout_mode = 2\n\n[node name=\"MarginContainer\" type=\"MarginContainer\" parent=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Keyboard\"]\nlayout_mode = 2\ntheme_override_constants/margin_left = 10\ntheme_override_constants/margin_top = 10\ntheme_override_constants/margin_right = 10\ntheme_override_constants/margin_bottom = 10\n\n[node name=\"ScrollContainer\" type=\"ScrollContainer\" parent=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Keyboard/MarginContainer\"]\nlayout_mode = 2\n\n[node name=\"KeyboardBindings\" type=\"VBoxContainer\" parent=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Keyboard/MarginContainer/ScrollContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 3\n\n[node name=\"Controller\" type=\"PanelContainer\" parent=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer\"]\nlayout_mode = 2\n\n[node name=\"MarginContainer\" type=\"MarginContainer\" parent=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Controller\"]\nlayout_mode = 2\ntheme_override_constants/margin_left = 10\ntheme_override_constants/margin_top = 10\ntheme_override_constants/margin_right = 10\ntheme_override_constants/margin_bottom = 10\n\n[node name=\"ScrollContainer\" type=\"ScrollContainer\" parent=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Controller/MarginContainer\"]\nlayout_mode = 2\n\n[node name=\"VBoxContainer\" type=\"VBoxContainer\" parent=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Controller/MarginContainer/ScrollContainer\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\n\n[node name=\"ControllerBindings\" type=\"VBoxContainer\" parent=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Controller/MarginContainer/ScrollContainer/VBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\nsize_flags_horizontal = 3\n\n[node name=\"Section\" parent=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Controller/MarginContainer/ScrollContainer/VBoxContainer\" instance=ExtResource(\"5_8v80s\")]\nlayout_mode = 2\ntext = \"Miscellaneous\"\n\n[node name=\"ControllerInvertHorizontal\" type=\"CheckBox\" parent=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Controller/MarginContainer/ScrollContainer/VBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntext = \"Invert horizontal movement\"\n\n[node name=\"ControllerInvertVertical\" type=\"CheckBox\" parent=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Controller/MarginContainer/ScrollContainer/VBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntext = \"Invert vertical movement\"\n\n[node name=\"HBoxContainer\" type=\"HBoxContainer\" parent=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Controller/MarginContainer/ScrollContainer/VBoxContainer\"]\nlayout_mode = 2\n\n[node name=\"DeadzoneLabel\" type=\"Label\" parent=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Controller/MarginContainer/ScrollContainer/VBoxContainer/HBoxContainer\"]\nlayout_mode = 2\ntext = \"Deadzone\"\n\n[node name=\"DeadzoneHSlider\" type=\"HSlider\" parent=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Controller/MarginContainer/ScrollContainer/VBoxContainer/HBoxContainer\"]\nunique_name_in_owner = true\ncustom_minimum_size = Vector2(400, 0)\nlayout_mode = 2\nsize_flags_vertical = 4\nmax_value = 0.99\nstep = 0.01\n\n[node name=\"DeadzoneSpinBox\" type=\"SpinBox\" parent=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Controller/MarginContainer/ScrollContainer/VBoxContainer/HBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\nfocus_mode = 2\nmax_value = 0.99\nstep = 0.01\n\n[node name=\"HBoxContainer\" type=\"HBoxContainer\" parent=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer\"]\nlayout_mode = 2\nmouse_filter = 2\n\n[node name=\"RichTextLabel\" type=\"RichTextLabel\" parent=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/HBoxContainer\"]\nlayout_mode = 2\nmouse_filter = 2\nfit_content = true\nscroll_active = false\nautowrap_mode = 0\nscript = ExtResource(\"10_xsw70\")\ninstructions_text = \"%s\"\nactions = Array[Resource(\"res://addons/guide/guide_action.gd\")]([ExtResource(\"11_uxwof\")])\n\n[node name=\"Control\" type=\"Control\" parent=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/HBoxContainer\"]\nlayout_mode = 2\nsize_flags_horizontal = 3\nmouse_filter = 2\n\n[node name=\"RichTextLabel2\" type=\"RichTextLabel\" parent=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/HBoxContainer\"]\nlayout_mode = 2\nmouse_filter = 2\nfit_content = true\nscroll_active = false\nautowrap_mode = 0\nscript = ExtResource(\"10_xsw70\")\ninstructions_text = \"%s\"\nactions = Array[Resource(\"res://addons/guide/guide_action.gd\")]([ExtResource(\"12_byojv\")])\n\n[node name=\"HBoxContainer\" type=\"HBoxContainer\" parent=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer\"]\nlayout_mode = 2\nalignment = 1\n\n[node name=\"ReturnToGame\" type=\"Button\" parent=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer\"]\nunique_name_in_owner = true\nlayout_mode = 2\ntext = \"Return to game\"\n\n[node name=\"GUIDEInputDetector\" type=\"Node\" parent=\".\"]\nunique_name_in_owner = true\neditor_description = \"This node has two inputs specified which count as abort for detection (Escape and Controller back button).\"\nscript = ExtResource(\"3_o0nvn\")\ndetection_countdown_seconds = 0.1\nabort_detection_on = Array[Resource(\"res://addons/guide/inputs/guide_input.gd\")]([SubResource(\"Resource_f3bao\"), SubResource(\"Resource_twrga\")])\n\n[node name=\"PressPrompt\" type=\"MarginContainer\" parent=\".\"]\nunique_name_in_owner = true\nvisible = false\nlayout_mode = 2\nmouse_filter = 0\n\n[node name=\"CenterContainer\" type=\"CenterContainer\" parent=\"PressPrompt\"]\nlayout_mode = 2\n\n[node name=\"Panel\" type=\"PanelContainer\" parent=\"PressPrompt/CenterContainer\"]\nlayout_mode = 2\ntheme_override_styles/panel = SubResource(\"StyleBoxFlat_bagfg\")\n\n[node name=\"MarginContainer\" type=\"MarginContainer\" parent=\"PressPrompt/CenterContainer/Panel\"]\nlayout_mode = 2\ntheme_override_constants/margin_left = 20\ntheme_override_constants/margin_top = 20\ntheme_override_constants/margin_right = 20\ntheme_override_constants/margin_bottom = 20\n\n[node name=\"Label\" type=\"Label\" parent=\"PressPrompt/CenterContainer/Panel/MarginContainer\"]\nlayout_mode = 2\ntext = \"Press new input...\"\n\n[connection signal=\"toggled\" from=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Controller/MarginContainer/ScrollContainer/VBoxContainer/ControllerInvertHorizontal\" to=\".\" method=\"_on_controller_invert_horizontal_toggled\"]\n[connection signal=\"toggled\" from=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Controller/MarginContainer/ScrollContainer/VBoxContainer/ControllerInvertVertical\" to=\".\" method=\"_on_controller_invert_vertical_toggled\"]\n[connection signal=\"value_changed\" from=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Controller/MarginContainer/ScrollContainer/VBoxContainer/HBoxContainer/DeadzoneHSlider\" to=\".\" method=\"_on_deadzone_h_slider_value_changed\"]\n[connection signal=\"value_changed\" from=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Controller/MarginContainer/ScrollContainer/VBoxContainer/HBoxContainer/DeadzoneSpinBox\" to=\".\" method=\"_on_deadzone_spin_box_value_changed\"]\n[connection signal=\"pressed\" from=\"CenterContainer/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/ReturnToGame\" to=\".\" method=\"_on_return_to_game_pressed\"]\n"
  },
  {
    "path": "guide_examples/remapping/utils.gd",
    "content": "const REMAPPING_CONFIG_PATH:String = \"user://remapping_config.tres\"\n\n# Constants for custom data that we store in the remapping config.\nconst CUSTOM_DATA_INVERT_HORIZONTAL:String = \"invert_horizontal\"\nconst CUSTOM_DATA_INVERT_VERTICAL:String = \"invert_vertical\"\nconst CUSTOM_DATA_MOVEMENT_DEADZONE:String = \"movement_deadzone\"\n\n\n## Loads the saved remapping config if it exists, or an empty remapping\n## config if none exists.\nstatic func load_remapping_config() -> GUIDERemappingConfig:\n\tif ResourceLoader.exists(REMAPPING_CONFIG_PATH):\n\t\treturn ResourceLoader.load(REMAPPING_CONFIG_PATH)\n\telse:\n\t\treturn GUIDERemappingConfig.new()\n\n## Saves the given remapping config to the user folder\nstatic func save_remapping_config(config:GUIDERemappingConfig) -> void:\n\tResourceSaver.save(config, REMAPPING_CONFIG_PATH)\n\tconfig.take_over_path(REMAPPING_CONFIG_PATH)\n"
  },
  {
    "path": "guide_examples/remapping/utils.gd.uid",
    "content": "uid://chnxcg46gaypy\n"
  },
  {
    "path": "guide_examples/shared/fireball/fireball.gd",
    "content": "extends Node2D\n\n@export var speed:float = 600\nvar direction:Vector2 = Vector2.ZERO\n\n\nfunc _ready() -> void:\n\tawait get_tree().create_timer(5).timeout\n\tqueue_free()\n\n\nfunc _process(delta:float) -> void:\n\tposition += speed * direction * delta\n"
  },
  {
    "path": "guide_examples/shared/fireball/fireball.gd.uid",
    "content": "uid://bvlq6u3twn6x2\n"
  },
  {
    "path": "guide_examples/shared/fireball/fireball.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://buu21kg4kkhiw\"\npath=\"res://.godot/imported/fireball.svg-da8480a7a8e47ac511e0971f6fa164bd.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://guide_examples/shared/fireball/fireball.svg\"\ndest_files=[\"res://.godot/imported/fireball.svg-da8480a7a8e47ac511e0971f6fa164bd.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=false\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "guide_examples/shared/fireball/fireball.tscn",
    "content": "[gd_scene load_steps=3 format=3 uid=\"uid://c36cnvgv2ur60\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/shared/fireball/fireball.gd\" id=\"1_jxcno\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://buu21kg4kkhiw\" path=\"res://guide_examples/shared/fireball/fireball.svg\" id=\"2_5ckxn\"]\n\n[node name=\"Fireball\" type=\"Node2D\"]\nscript = ExtResource(\"1_jxcno\")\n\n[node name=\"Fireball\" type=\"Sprite2D\" parent=\".\"]\ntexture = ExtResource(\"2_5ckxn\")\n"
  },
  {
    "path": "guide_examples/shared/godot_logo.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://byjxtsekdl8t2\"\npath=\"res://.godot/imported/godot_logo.svg-ce7d52346b74cfa0766735b0b77afab9.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://guide_examples/shared/godot_logo.svg\"\ndest_files=[\"res://.godot/imported/godot_logo.svg-ce7d52346b74cfa0766735b0b77afab9.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=false\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "guide_examples/shared/instructions_label.gd",
    "content": "## This is an example for how input prompts can be shown to the player. \nextends RichTextLabel\n\n## The instructions text. Should contain %s where the action text should go. \n@export_multiline var instructions_text:String\n## The actions which should be used for rendering the instructions. One action for \n## each %s in the text.\n@export var actions:Array[GUIDEAction] = []\n## The icon size to be used for rendering.\n@export var icon_size:int = 48\n\n## If set, the label will only show when the given mapping context is active.\n@export var limit_to_context:GUIDEMappingContext\n\n# The formatter. This will do the actual work of formatting action inputs into prompts.\nvar _formatter:GUIDEInputFormatter\n\nfunc _ready() -> void:\n\tbbcode_enabled = true\n\tfit_content = true\n\tscroll_active = false\n\tautowrap_mode = TextServer.AUTOWRAP_OFF\n\t\n\t# Subscribe to the input mappings change so we can update the prompts or hide the label\n\t# when any inputs change. This way the label can automatically update itself if we switch\n\t# from keyboard to controller input or rebind some keys.\n\tGUIDE.input_mappings_changed.connect(_update_instructions)\n\t_formatter = GUIDEInputFormatter.for_active_contexts(icon_size)\n\t\n\t\nfunc _update_instructions() -> void:\n\t# If we only show for a certain context, hide if that context isn't active right now.\n\tif limit_to_context != null and not GUIDE.is_mapping_context_enabled(limit_to_context):\n\t\tvisible = false\n\t\treturn\n\t\t\n\t# if no mapping context is active, we'll not be able to show instructions, so bail out here.\t\n\tif GUIDE.get_enabled_mapping_contexts().is_empty():\n\t\tvisible = false\n\t\treturn\n\t\t\n\tvisible = true\n\t\n\t# Update the prompts.\n\tvar replacements:Array[String] = []\n\tfor action in actions:\n\t\treplacements.append(await _formatter.action_as_richtext_async(action))\n\t\n\tparse_bbcode(instructions_text % replacements)\n\t\n\n\n"
  },
  {
    "path": "guide_examples/shared/instructions_label.gd.uid",
    "content": "uid://w8q1xlqw60qh\n"
  },
  {
    "path": "guide_examples/shared/ui_theme.tres",
    "content": "[gd_resource type=\"Theme\" format=3 uid=\"uid://dot0gi1yoqmrl\"]\n\n[resource]\ndefault_font_size = 24\nHeaderLarge/font_sizes/font_size = 30\n"
  },
  {
    "path": "guide_examples/simple_input/mapping_contexts/move_down.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://cjfdsja54wtus\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_yaxge\"]\n\n[resource]\nscript = ExtResource(\"1_yaxge\")\nname = \"\"\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/simple_input/mapping_contexts/move_left.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://b7rcj2usvx7iu\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_2lu1r\"]\n\n[resource]\nscript = ExtResource(\"1_2lu1r\")\nname = \"\"\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/simple_input/mapping_contexts/move_right.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://cn4nypfrnusrn\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_o2mf7\"]\n\n[resource]\nscript = ExtResource(\"1_o2mf7\")\nname = \"\"\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/simple_input/mapping_contexts/move_up.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://cjohgt5cdoxvd\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_1p8w8\"]\n\n[resource]\nscript = ExtResource(\"1_1p8w8\")\nname = \"\"\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/simple_input/mapping_contexts/simple_input.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMappingContext\" load_steps=21 format=3 uid=\"uid://c161ru5ubsvm5\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_mapping_context.gd\" id=\"1_1q401\"]\n[ext_resource type=\"Resource\" uid=\"uid://cjohgt5cdoxvd\" path=\"res://guide_examples/simple_input/mapping_contexts/move_up.tres\" id=\"1_p6dvd\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_key.gd\" id=\"2_dmcv6\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_input_mapping.gd\" id=\"3_j3d4x\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action_mapping.gd\" id=\"4_5ymhe\"]\n[ext_resource type=\"Resource\" uid=\"uid://cjfdsja54wtus\" path=\"res://guide_examples/simple_input/mapping_contexts/move_down.tres\" id=\"5_x3j1d\"]\n[ext_resource type=\"Resource\" uid=\"uid://b7rcj2usvx7iu\" path=\"res://guide_examples/simple_input/mapping_contexts/move_left.tres\" id=\"6_3n2n7\"]\n[ext_resource type=\"Resource\" uid=\"uid://cn4nypfrnusrn\" path=\"res://guide_examples/simple_input/mapping_contexts/move_right.tres\" id=\"7_mgx2j\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_x4hy0\"]\nscript = ExtResource(\"2_dmcv6\")\nkey = 87\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_skv88\"]\nscript = ExtResource(\"3_j3d4x\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_x4hy0\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_13yll\"]\nscript = ExtResource(\"4_5ymhe\")\naction = ExtResource(\"1_p6dvd\")\ninput_mappings = Array[ExtResource(\"3_j3d4x\")]([SubResource(\"Resource_skv88\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_kjfiu\"]\nscript = ExtResource(\"2_dmcv6\")\nkey = 83\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_11cob\"]\nscript = ExtResource(\"3_j3d4x\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_kjfiu\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_olvia\"]\nscript = ExtResource(\"4_5ymhe\")\naction = ExtResource(\"5_x3j1d\")\ninput_mappings = Array[ExtResource(\"3_j3d4x\")]([SubResource(\"Resource_11cob\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_rkxlb\"]\nscript = ExtResource(\"2_dmcv6\")\nkey = 65\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_j7qak\"]\nscript = ExtResource(\"3_j3d4x\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_rkxlb\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_gs5ov\"]\nscript = ExtResource(\"4_5ymhe\")\naction = ExtResource(\"6_3n2n7\")\ninput_mappings = Array[ExtResource(\"3_j3d4x\")]([SubResource(\"Resource_j7qak\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_jek7t\"]\nscript = ExtResource(\"2_dmcv6\")\nkey = 68\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_m4m5w\"]\nscript = ExtResource(\"3_j3d4x\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_jek7t\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_hk2n5\"]\nscript = ExtResource(\"4_5ymhe\")\naction = ExtResource(\"7_mgx2j\")\ninput_mappings = Array[ExtResource(\"3_j3d4x\")]([SubResource(\"Resource_m4m5w\")])\n\n[resource]\nscript = ExtResource(\"1_1q401\")\ndisplay_name = \"\"\nmappings = Array[ExtResource(\"4_5ymhe\")]([SubResource(\"Resource_13yll\"), SubResource(\"Resource_olvia\"), SubResource(\"Resource_gs5ov\"), SubResource(\"Resource_hk2n5\")])\n"
  },
  {
    "path": "guide_examples/simple_input/player.gd",
    "content": "extends Node2D\n\n@export var speed:float = 100\n\n@export var left_action:GUIDEAction\n@export var right_action:GUIDEAction\n@export var up_action:GUIDEAction\n@export var down_action:GUIDEAction\n\nfunc _process(delta:float) -> void:\n\t# This is close to how input would be handled with Godot's built-in\n\t# input. GUIDE can actually combine the input into a 2D axis for you\n\t# (similar to Godot's Input.get_vector). Because this is\n\t# done in the mapping context, the script doesn't need to know about\n\t# it. Look at the 2d_axis_mapping example to see how to streamline\n\t# this code quite a bit.\n\t\n\tvar offset:Vector2 = Vector2.ZERO\n\t\n\tif left_action.is_triggered():\n\t\toffset.x = -1\n\t\n\tif right_action.is_triggered():\n\t\toffset.x = 1\n\t\t\n\tif up_action.is_triggered():\n\t\toffset.y = -1\n\t\t\n\tif down_action.is_triggered():\n\t\toffset.y = 1\n\t\t\n\tposition += offset * speed * delta\n"
  },
  {
    "path": "guide_examples/simple_input/player.gd.uid",
    "content": "uid://dhpub7a1ixyxj\n"
  },
  {
    "path": "guide_examples/simple_input/simple_input.gd",
    "content": "extends Node\n\n@export var mapping_context:GUIDEMappingContext\n\nfunc _ready() -> void:\n\tGUIDE.enable_mapping_context(mapping_context)\n"
  },
  {
    "path": "guide_examples/simple_input/simple_input.gd.uid",
    "content": "uid://dej0g6ye7nfxi\n"
  },
  {
    "path": "guide_examples/simple_input/simple_input.tscn",
    "content": "[gd_scene load_steps=12 format=3 uid=\"uid://cchyatnt0wl5x\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/simple_input/simple_input.gd\" id=\"1_cgpw0\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://byjxtsekdl8t2\" path=\"res://guide_examples/shared/godot_logo.svg\" id=\"1_t8vwd\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/simple_input/player.gd\" id=\"2_o6vg3\"]\n[ext_resource type=\"Resource\" uid=\"uid://c161ru5ubsvm5\" path=\"res://guide_examples/simple_input/mapping_contexts/simple_input.tres\" id=\"3_2b24x\"]\n[ext_resource type=\"Resource\" uid=\"uid://b7rcj2usvx7iu\" path=\"res://guide_examples/simple_input/mapping_contexts/move_left.tres\" id=\"4_50on6\"]\n[ext_resource type=\"Resource\" uid=\"uid://cn4nypfrnusrn\" path=\"res://guide_examples/simple_input/mapping_contexts/move_right.tres\" id=\"5_47yin\"]\n[ext_resource type=\"Resource\" uid=\"uid://cjohgt5cdoxvd\" path=\"res://guide_examples/simple_input/mapping_contexts/move_up.tres\" id=\"6_b7h5t\"]\n[ext_resource type=\"Resource\" uid=\"uid://cjfdsja54wtus\" path=\"res://guide_examples/simple_input/mapping_contexts/move_down.tres\" id=\"7_4pdc5\"]\n[ext_resource type=\"Theme\" uid=\"uid://dot0gi1yoqmrl\" path=\"res://guide_examples/shared/ui_theme.tres\" id=\"8_tko8y\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/shared/instructions_label.gd\" id=\"9_hqrkx\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://dkr80d2pi0d41\" path=\"res://addons/guide/debugger/guide_debugger.tscn\" id=\"10_3ap6u\"]\n\n[node name=\"SimpleInput\" type=\"Node\"]\nscript = ExtResource(\"1_cgpw0\")\nmapping_context = ExtResource(\"3_2b24x\")\n\n[node name=\"Player\" type=\"Sprite2D\" parent=\".\"]\nposition = Vector2(546, 317)\ntexture = ExtResource(\"1_t8vwd\")\nscript = ExtResource(\"2_o6vg3\")\nspeed = 300.0\nleft_action = ExtResource(\"4_50on6\")\nright_action = ExtResource(\"5_47yin\")\nup_action = ExtResource(\"6_b7h5t\")\ndown_action = ExtResource(\"7_4pdc5\")\n\n[node name=\"UI Layer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"Instructions Label\" type=\"RichTextLabel\" parent=\"UI Layer\"]\nanchors_preset = 1\nanchor_left = 1.0\nanchor_right = 1.0\noffset_left = -286.0\noffset_top = 24.0\noffset_right = -39.0\noffset_bottom = 47.0\ngrow_horizontal = 0\ntheme = ExtResource(\"8_tko8y\")\nscript = ExtResource(\"9_hqrkx\")\ninstructions_text = \"%s to move left.\n%s to move right.\n%s to move up.\n%s to move down.\"\nactions = Array[Resource(\"res://addons/guide/guide_action.gd\")]([ExtResource(\"4_50on6\"), ExtResource(\"5_47yin\"), ExtResource(\"6_b7h5t\"), ExtResource(\"7_4pdc5\")])\nmetadata/_edit_use_anchors_ = true\n\n[node name=\"Debug Layer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"GuideDebugger\" parent=\"Debug Layer\" instance=ExtResource(\"10_3ap6u\")]\ntheme = ExtResource(\"8_tko8y\")\nmetadata/_edit_lock_ = true\n"
  },
  {
    "path": "guide_examples/tap_and_hold/mapping_contexts/jump.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://djow080f02fos\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_dxeu7\"]\n\n[resource]\nscript = ExtResource(\"1_dxeu7\")\nname = &\"\"\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/tap_and_hold/mapping_contexts/somersault.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://dcdlaiw50k4t3\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_mgxtb\"]\n\n[resource]\nscript = ExtResource(\"1_mgxtb\")\nname = \"\"\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/tap_and_hold/mapping_contexts/tap_and_hold.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMappingContext\" load_steps=17 format=3 uid=\"uid://dkorp45s6fjqk\"]\n\n[ext_resource type=\"Resource\" uid=\"uid://djow080f02fos\" path=\"res://guide_examples/tap_and_hold/mapping_contexts/jump.tres\" id=\"1_pwkn3\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_key.gd\" id=\"2_6utl5\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_input_mapping.gd\" id=\"3_sfabf\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_tap.gd\" id=\"4_0qfc2\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action_mapping.gd\" id=\"4_nrdxn\"]\n[ext_resource type=\"Resource\" uid=\"uid://dcdlaiw50k4t3\" path=\"res://guide_examples/tap_and_hold/mapping_contexts/somersault.tres\" id=\"6_5qh27\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_hold.gd\" id=\"7_sj5n7\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_mapping_context.gd\" id=\"8_q4q7m\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_fvatf\"]\nscript = ExtResource(\"2_6utl5\")\nkey = 32\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_k188c\"]\nscript = ExtResource(\"4_0qfc2\")\ntap_threshold = 0.2\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_tg2ja\"]\nscript = ExtResource(\"3_sfabf\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_fvatf\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_k188c\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_ghgfq\"]\nscript = ExtResource(\"4_nrdxn\")\naction = ExtResource(\"1_pwkn3\")\ninput_mappings = Array[ExtResource(\"3_sfabf\")]([SubResource(\"Resource_tg2ja\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_75xxu\"]\nscript = ExtResource(\"2_6utl5\")\nkey = 32\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_dum1i\"]\nscript = ExtResource(\"7_sj5n7\")\nhold_treshold = 0.5\nis_one_shot = true\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_tbykh\"]\nscript = ExtResource(\"3_sfabf\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_75xxu\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_dum1i\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_5u3vu\"]\nscript = ExtResource(\"4_nrdxn\")\naction = ExtResource(\"6_5qh27\")\ninput_mappings = Array[ExtResource(\"3_sfabf\")]([SubResource(\"Resource_tbykh\")])\n\n[resource]\nscript = ExtResource(\"8_q4q7m\")\ndisplay_name = \"\"\nmappings = Array[ExtResource(\"4_nrdxn\")]([SubResource(\"Resource_ghgfq\"), SubResource(\"Resource_5u3vu\")])\n"
  },
  {
    "path": "guide_examples/tap_and_hold/player.gd",
    "content": "extends Node2D\n\n\n@onready var _animation_player:AnimationPlayer = %AnimationPlayer\n@onready var _progress_bar:ProgressBar = %ProgressBar\n\n@export var jump_action:GUIDEAction\n@export var somersault_action:GUIDEAction\n\nfunc _ready() -> void:\n\tjump_action.triggered.connect(_play.bind(\"jump\"))\n\tsomersault_action.triggered.connect(_play.bind(\"somersault\"))\n\tsomersault_action.ongoing.connect(_update_progress_bar)\n\tsomersault_action.triggered.connect(_hide_progress_bar)\n\tsomersault_action.cancelled.connect(_hide_progress_bar)\n\t\nfunc _play(animation:String) -> void:\n\tif _animation_player.is_playing():\n\t\treturn\n\t\t\n\t_animation_player.play(animation)\n\t\nfunc _update_progress_bar() -> void:\n\t# exceeds tap time\n\tif somersault_action.elapsed_seconds > 0.1:\n\t\t_progress_bar.value = somersault_action.elapsed_ratio\n\t\t_progress_bar.visible = true\n\nfunc _hide_progress_bar() -> void:\n\t_progress_bar.visible = false\n"
  },
  {
    "path": "guide_examples/tap_and_hold/player.gd.uid",
    "content": "uid://brd1oa6q1qsl3\n"
  },
  {
    "path": "guide_examples/tap_and_hold/tap_and_hold.gd",
    "content": "extends Node\n\n@export var mapping_context:GUIDEMappingContext\n\nfunc _ready() -> void:\n\tGUIDE.enable_mapping_context(mapping_context)\n"
  },
  {
    "path": "guide_examples/tap_and_hold/tap_and_hold.gd.uid",
    "content": "uid://dpkjsi6bk64ly\n"
  },
  {
    "path": "guide_examples/tap_and_hold/tap_and_hold.tscn",
    "content": "[gd_scene load_steps=14 format=3 uid=\"uid://bq1y86drsbsgc\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/tap_and_hold/tap_and_hold.gd\" id=\"1_ek3h7\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://byjxtsekdl8t2\" path=\"res://guide_examples/shared/godot_logo.svg\" id=\"1_segxn\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/tap_and_hold/player.gd\" id=\"2_gpfh8\"]\n[ext_resource type=\"Resource\" uid=\"uid://dkorp45s6fjqk\" path=\"res://guide_examples/tap_and_hold/mapping_contexts/tap_and_hold.tres\" id=\"3_u8e88\"]\n[ext_resource type=\"Resource\" uid=\"uid://djow080f02fos\" path=\"res://guide_examples/tap_and_hold/mapping_contexts/jump.tres\" id=\"4_8qeav\"]\n[ext_resource type=\"Resource\" uid=\"uid://dcdlaiw50k4t3\" path=\"res://guide_examples/tap_and_hold/mapping_contexts/somersault.tres\" id=\"5_wp1cr\"]\n[ext_resource type=\"Theme\" uid=\"uid://dot0gi1yoqmrl\" path=\"res://guide_examples/shared/ui_theme.tres\" id=\"6_r6oud\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://dkr80d2pi0d41\" path=\"res://addons/guide/debugger/guide_debugger.tscn\" id=\"6_vjlt4\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/shared/instructions_label.gd\" id=\"7_304xo\"]\n\n[sub_resource type=\"Animation\" id=\"Animation_j3lvc\"]\nlength = 0.001\ntracks/0/type = \"value\"\ntracks/0/imported = false\ntracks/0/enabled = true\ntracks/0/path = NodePath(\"Player:position\")\ntracks/0/interp = 1\ntracks/0/loop_wrap = true\ntracks/0/keys = {\n\"times\": PackedFloat32Array(0),\n\"transitions\": PackedFloat32Array(1),\n\"update\": 0,\n\"values\": [Vector2(546, 317)]\n}\ntracks/1/type = \"value\"\ntracks/1/imported = false\ntracks/1/enabled = true\ntracks/1/path = NodePath(\"Player:rotation\")\ntracks/1/interp = 1\ntracks/1/loop_wrap = true\ntracks/1/keys = {\n\"times\": PackedFloat32Array(0),\n\"transitions\": PackedFloat32Array(1),\n\"update\": 0,\n\"values\": [0.0]\n}\n\n[sub_resource type=\"Animation\" id=\"Animation_a86xu\"]\nresource_name = \"jump\"\nlength = 0.5\ntracks/0/type = \"value\"\ntracks/0/imported = false\ntracks/0/enabled = true\ntracks/0/path = NodePath(\"Player:position\")\ntracks/0/interp = 1\ntracks/0/loop_wrap = true\ntracks/0/keys = {\n\"times\": PackedFloat32Array(0, 0.2, 0.5),\n\"transitions\": PackedFloat32Array(1, 1, 1),\n\"update\": 0,\n\"values\": [Vector2(546, 317), Vector2(546, 260), Vector2(546, 317)]\n}\n\n[sub_resource type=\"Animation\" id=\"Animation_fir1a\"]\nresource_name = \"somersault\"\ntracks/0/type = \"value\"\ntracks/0/imported = false\ntracks/0/enabled = true\ntracks/0/path = NodePath(\"Player:position\")\ntracks/0/interp = 1\ntracks/0/loop_wrap = true\ntracks/0/keys = {\n\"times\": PackedFloat32Array(0, 0.5, 1),\n\"transitions\": PackedFloat32Array(1, 1, 1),\n\"update\": 0,\n\"values\": [Vector2(546, 317), Vector2(546, 200), Vector2(546, 317)]\n}\ntracks/1/type = \"value\"\ntracks/1/imported = false\ntracks/1/enabled = true\ntracks/1/path = NodePath(\"Player:rotation\")\ntracks/1/interp = 1\ntracks/1/loop_wrap = true\ntracks/1/keys = {\n\"times\": PackedFloat32Array(0, 0.3, 0.5, 0.7),\n\"transitions\": PackedFloat32Array(1, 1, 1, 1),\n\"update\": 0,\n\"values\": [0.0, 0.0, -3.14159, -6.28319]\n}\n\n[sub_resource type=\"AnimationLibrary\" id=\"AnimationLibrary_8sm1c\"]\n_data = {\n\"RESET\": SubResource(\"Animation_j3lvc\"),\n\"jump\": SubResource(\"Animation_a86xu\"),\n\"somersault\": SubResource(\"Animation_fir1a\")\n}\n\n[node name=\"TapAndHold\" type=\"Node\"]\nscript = ExtResource(\"1_ek3h7\")\nmapping_context = ExtResource(\"3_u8e88\")\n\n[node name=\"Player\" type=\"Sprite2D\" parent=\".\"]\nposition = Vector2(546, 317)\ntexture = ExtResource(\"1_segxn\")\nscript = ExtResource(\"2_gpfh8\")\njump_action = ExtResource(\"4_8qeav\")\nsomersault_action = ExtResource(\"5_wp1cr\")\n\n[node name=\"ProgressBar\" type=\"ProgressBar\" parent=\"Player\"]\nunique_name_in_owner = true\nvisible = false\noffset_left = -58.0\noffset_top = 64.0\noffset_right = 57.0\noffset_bottom = 91.0\nmax_value = 1.0\nshow_percentage = false\n\n[node name=\"AnimationPlayer\" type=\"AnimationPlayer\" parent=\".\"]\nunique_name_in_owner = true\nlibraries = {\n\"\": SubResource(\"AnimationLibrary_8sm1c\")\n}\n\n[node name=\"UI Layer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"Instructions Label\" type=\"RichTextLabel\" parent=\"UI Layer\"]\nanchors_preset = 1\nanchor_left = 1.0\nanchor_right = 1.0\noffset_left = -473.0\noffset_top = 527.0\noffset_right = -226.0\noffset_bottom = 550.0\ngrow_horizontal = 0\ntheme = ExtResource(\"6_r6oud\")\nscript = ExtResource(\"7_304xo\")\ninstructions_text = \"Tap %s to jump.\nHold %s to somersault.\"\nactions = Array[Resource(\"res://addons/guide/guide_action.gd\")]([ExtResource(\"4_8qeav\"), ExtResource(\"5_wp1cr\")])\nmetadata/_edit_use_anchors_ = true\n\n[node name=\"Label\" type=\"Label\" parent=\"UI Layer\"]\nvisible = false\nanchors_preset = 1\nanchor_left = 1.0\nanchor_right = 1.0\noffset_left = -660.0\noffset_top = 71.0\noffset_right = -57.0\noffset_bottom = 223.0\ngrow_horizontal = 0\ntheme = ExtResource(\"6_r6oud\")\ntext = \"This demonstrates using the hold and tap triggers to bind multiple actions to the same key.  This also uses the hold action's elapsed_ratio property to drive a hold progress bar to indicate how long the key needs to be held.\"\nautowrap_mode = 2\n\n[node name=\"Debug Layer\" type=\"CanvasLayer\" parent=\".\"]\nvisible = false\n\n[node name=\"GuideDebugger\" parent=\"Debug Layer\" instance=ExtResource(\"6_vjlt4\")]\ntheme = ExtResource(\"6_r6oud\")\nmetadata/_edit_lock_ = true\n"
  },
  {
    "path": "guide_examples/top_down_shooter/bolt/bolt.gd",
    "content": "extends Sprite2D\n\n@export var speed:float = 500\n@export var lifetime:float = 1.0\n\nfunc _process(delta: float) -> void:\n\tposition += transform.x * delta * speed\n\tlifetime -= delta\n\tif lifetime <= 0:\n\t\tqueue_free()\n"
  },
  {
    "path": "guide_examples/top_down_shooter/bolt/bolt.gd.uid",
    "content": "uid://w7nan374op8\n"
  },
  {
    "path": "guide_examples/top_down_shooter/bolt/bolt.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://dfpdumvnepffe\"\npath=\"res://.godot/imported/bolt.png-d2b4175f016737de6380cafdf09c07c0.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://guide_examples/top_down_shooter/bolt/bolt.png\"\ndest_files=[\"res://.godot/imported/bolt.png-d2b4175f016737de6380cafdf09c07c0.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "guide_examples/top_down_shooter/bolt/bolt.tscn",
    "content": "[gd_scene load_steps=3 format=3 uid=\"uid://c0fqv8e3tljmp\"]\n\n[ext_resource type=\"Texture2D\" uid=\"uid://dfpdumvnepffe\" path=\"res://guide_examples/top_down_shooter/bolt/bolt.png\" id=\"1_b5s4m\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/top_down_shooter/bolt/bolt.gd\" id=\"2_o61jt\"]\n\n[node name=\"Bolt\" type=\"Sprite2D\"]\ntexture = ExtResource(\"1_b5s4m\")\nscript = ExtResource(\"2_o61jt\")\nspeed = 1000.0\nlifetime = 2.0\n"
  },
  {
    "path": "guide_examples/top_down_shooter/mapping_contexts/controller.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMappingContext\" load_steps=28 format=3 uid=\"uid://dysrgn1ubf15g\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_mapping_context.gd\" id=\"1_3vbsc\"]\n[ext_resource type=\"Resource\" uid=\"uid://ch070wegl722t\" path=\"res://guide_examples/top_down_shooter/mapping_contexts/move.tres\" id=\"1_epjja\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_joy_axis_2d.gd\" id=\"2_bwpvf\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_input_mapping.gd\" id=\"2_skach\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_deadzone.gd\" id=\"3_872fr\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action_mapping.gd\" id=\"3_e36mu\"]\n[ext_resource type=\"Resource\" uid=\"uid://vpsh1myp67ws\" path=\"res://guide_examples/top_down_shooter/mapping_contexts/look_relative.tres\" id=\"6_5clnx\"]\n[ext_resource type=\"Resource\" uid=\"uid://coktqyup12g3w\" path=\"res://guide_examples/top_down_shooter/mapping_contexts/fire.tres\" id=\"7_pfi7b\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_joy_axis_1d.gd\" id=\"8_gtkor\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_pressed.gd\" id=\"9_i3uls\"]\n[ext_resource type=\"Resource\" uid=\"uid://bnd2jguy7tfti\" path=\"res://guide_examples/top_down_shooter/mapping_contexts/switch_to_keyboard_and_mouse.tres\" id=\"10_m7sth\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_any.gd\" id=\"11_jpwmq\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_uwau4\"]\nscript = ExtResource(\"2_bwpvf\")\nx = 0\ny = 1\njoy_index = -1\n\n[sub_resource type=\"Resource\" id=\"Resource_i1fx3\"]\nscript = ExtResource(\"3_872fr\")\nlower_threshold = 0.2\nupper_threshold = 1.0\n\n[sub_resource type=\"Resource\" id=\"Resource_7mv4j\"]\nscript = ExtResource(\"2_skach\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_uwau4\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_i1fx3\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_x8yon\"]\nscript = ExtResource(\"3_e36mu\")\naction = ExtResource(\"1_epjja\")\ninput_mappings = Array[ExtResource(\"2_skach\")]([SubResource(\"Resource_7mv4j\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_2nugj\"]\nscript = ExtResource(\"2_bwpvf\")\nx = 2\ny = 3\njoy_index = -1\n\n[sub_resource type=\"Resource\" id=\"Resource_iln10\"]\nscript = ExtResource(\"3_872fr\")\nlower_threshold = 0.607\nupper_threshold = 1.0\n\n[sub_resource type=\"Resource\" id=\"Resource_yxygj\"]\nscript = ExtResource(\"2_skach\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_2nugj\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_iln10\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_ropdf\"]\nscript = ExtResource(\"3_e36mu\")\naction = ExtResource(\"6_5clnx\")\ninput_mappings = Array[ExtResource(\"2_skach\")]([SubResource(\"Resource_yxygj\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_mnoq7\"]\nscript = ExtResource(\"8_gtkor\")\naxis = 5\njoy_index = -1\n\n[sub_resource type=\"Resource\" id=\"Resource_7u5im\"]\nscript = ExtResource(\"9_i3uls\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_ssica\"]\nscript = ExtResource(\"2_skach\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_mnoq7\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_7u5im\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_a7lt0\"]\nscript = ExtResource(\"3_e36mu\")\naction = ExtResource(\"7_pfi7b\")\ninput_mappings = Array[ExtResource(\"2_skach\")]([SubResource(\"Resource_ssica\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_konxq\"]\nscript = ExtResource(\"11_jpwmq\")\nmouse_buttons = true\nmouse_movement = true\nminimum_mouse_movement_distance = 1.0\njoy_buttons = false\njoy_axes = false\nminimum_joy_axis_actuation_strength = 0.2\nkeyboard = true\ntouch = false\nmouse = true\njoy = false\n\n[sub_resource type=\"Resource\" id=\"Resource_lid0f\"]\nscript = ExtResource(\"2_skach\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_konxq\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_6hier\"]\nscript = ExtResource(\"3_e36mu\")\naction = ExtResource(\"10_m7sth\")\ninput_mappings = Array[ExtResource(\"2_skach\")]([SubResource(\"Resource_lid0f\")])\n\n[resource]\nscript = ExtResource(\"1_3vbsc\")\ndisplay_name = \"\"\nmappings = Array[ExtResource(\"3_e36mu\")]([SubResource(\"Resource_x8yon\"), SubResource(\"Resource_ropdf\"), SubResource(\"Resource_a7lt0\"), SubResource(\"Resource_6hier\")])\n"
  },
  {
    "path": "guide_examples/top_down_shooter/mapping_contexts/fire.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://coktqyup12g3w\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_jmn08\"]\n\n[resource]\nscript = ExtResource(\"1_jmn08\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/top_down_shooter/mapping_contexts/keyboard_and_mouse.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMappingContext\" load_steps=40 format=3 uid=\"uid://dhp7nup127wxw\"]\n\n[ext_resource type=\"Resource\" uid=\"uid://ch070wegl722t\" path=\"res://guide_examples/top_down_shooter/mapping_contexts/move.tres\" id=\"1_o40br\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_key.gd\" id=\"2_r8xkm\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_negate.gd\" id=\"3_mxuhd\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_input_mapping.gd\" id=\"4_nld8w\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_input_swizzle.gd\" id=\"4_sdo77\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action_mapping.gd\" id=\"5_wiw4m\"]\n[ext_resource type=\"Resource\" uid=\"uid://c4tpipfhludsi\" path=\"res://guide_examples/top_down_shooter/mapping_contexts/look_absolute.tres\" id=\"7_4uvlp\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_mapping_context.gd\" id=\"7_dx1p2\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_mouse_position.gd\" id=\"8_hys4n\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_canvas_coordinates.gd\" id=\"9_5f66x\"]\n[ext_resource type=\"Resource\" uid=\"uid://coktqyup12g3w\" path=\"res://guide_examples/top_down_shooter/mapping_contexts/fire.tres\" id=\"10_he1ky\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_mouse_button.gd\" id=\"11_btbxh\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_pressed.gd\" id=\"12_ce85k\"]\n[ext_resource type=\"Resource\" uid=\"uid://byu565ktximg2\" path=\"res://guide_examples/top_down_shooter/mapping_contexts/switch_to_controller.tres\" id=\"13_0evsu\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_any.gd\" id=\"14_ulkgf\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_rqldx\"]\nscript = ExtResource(\"2_r8xkm\")\nkey = 87\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_fedub\"]\nscript = ExtResource(\"4_sdo77\")\norder = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_sithj\"]\nscript = ExtResource(\"3_mxuhd\")\nx = true\ny = true\nz = true\n\n[sub_resource type=\"Resource\" id=\"Resource_gvylm\"]\nscript = ExtResource(\"4_nld8w\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_rqldx\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_fedub\"), SubResource(\"Resource_sithj\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_2grck\"]\nscript = ExtResource(\"2_r8xkm\")\nkey = 83\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_e6ufd\"]\nscript = ExtResource(\"4_sdo77\")\norder = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_hclrp\"]\nscript = ExtResource(\"4_nld8w\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_2grck\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_e6ufd\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_0e6tb\"]\nscript = ExtResource(\"2_r8xkm\")\nkey = 65\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_o7bio\"]\nscript = ExtResource(\"3_mxuhd\")\nx = true\ny = true\nz = true\n\n[sub_resource type=\"Resource\" id=\"Resource_085kd\"]\nscript = ExtResource(\"4_nld8w\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_0e6tb\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_o7bio\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_h42bk\"]\nscript = ExtResource(\"2_r8xkm\")\nkey = 68\nshift = false\ncontrol = false\nalt = false\nmeta = false\nallow_additional_modifiers = true\n\n[sub_resource type=\"Resource\" id=\"Resource_vnoud\"]\nscript = ExtResource(\"4_nld8w\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_h42bk\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_x8yon\"]\nscript = ExtResource(\"5_wiw4m\")\naction = ExtResource(\"1_o40br\")\ninput_mappings = Array[ExtResource(\"4_nld8w\")]([SubResource(\"Resource_gvylm\"), SubResource(\"Resource_hclrp\"), SubResource(\"Resource_085kd\"), SubResource(\"Resource_vnoud\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_dgqq1\"]\nscript = ExtResource(\"8_hys4n\")\n\n[sub_resource type=\"Resource\" id=\"Resource_qegvf\"]\nscript = ExtResource(\"9_5f66x\")\nrelative_input = false\n\n[sub_resource type=\"Resource\" id=\"Resource_yxygj\"]\nscript = ExtResource(\"4_nld8w\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_dgqq1\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_qegvf\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_ropdf\"]\nscript = ExtResource(\"5_wiw4m\")\naction = ExtResource(\"7_4uvlp\")\ninput_mappings = Array[ExtResource(\"4_nld8w\")]([SubResource(\"Resource_yxygj\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_omwno\"]\nscript = ExtResource(\"11_btbxh\")\nbutton = 1\n\n[sub_resource type=\"Resource\" id=\"Resource_7jcut\"]\nscript = ExtResource(\"12_ce85k\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_4uxij\"]\nscript = ExtResource(\"4_nld8w\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_omwno\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_7jcut\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_8w3dk\"]\nscript = ExtResource(\"5_wiw4m\")\naction = ExtResource(\"10_he1ky\")\ninput_mappings = Array[ExtResource(\"4_nld8w\")]([SubResource(\"Resource_4uxij\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_nqj7r\"]\nscript = ExtResource(\"14_ulkgf\")\nmouse_buttons = false\nmouse_movement = false\nminimum_mouse_movement_distance = 1.0\njoy_buttons = true\njoy_axes = true\nminimum_joy_axis_actuation_strength = 0.5\nkeyboard = false\ntouch = false\nmouse = false\njoy = true\n\n[sub_resource type=\"Resource\" id=\"Resource_bk656\"]\nscript = ExtResource(\"4_nld8w\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_nqj7r\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_ewfkl\"]\nscript = ExtResource(\"5_wiw4m\")\naction = ExtResource(\"13_0evsu\")\ninput_mappings = Array[ExtResource(\"4_nld8w\")]([SubResource(\"Resource_bk656\")])\n\n[resource]\nscript = ExtResource(\"7_dx1p2\")\ndisplay_name = \"\"\nmappings = Array[ExtResource(\"5_wiw4m\")]([SubResource(\"Resource_x8yon\"), SubResource(\"Resource_ropdf\"), SubResource(\"Resource_8w3dk\"), SubResource(\"Resource_ewfkl\")])\n"
  },
  {
    "path": "guide_examples/top_down_shooter/mapping_contexts/look_absolute.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://c4tpipfhludsi\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_ujbw2\"]\n\n[resource]\nscript = ExtResource(\"1_ujbw2\")\nname = &\"\"\naction_value_type = 2\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/top_down_shooter/mapping_contexts/look_relative.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://vpsh1myp67ws\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_upkuf\"]\n\n[resource]\nscript = ExtResource(\"1_upkuf\")\nname = &\"\"\naction_value_type = 2\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/top_down_shooter/mapping_contexts/move.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://ch070wegl722t\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_w0ans\"]\n\n[resource]\nscript = ExtResource(\"1_w0ans\")\nname = &\"\"\naction_value_type = 2\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/top_down_shooter/mapping_contexts/switch_to_controller.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://byu565ktximg2\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_ghtdg\"]\n\n[resource]\nscript = ExtResource(\"1_ghtdg\")\nname = &\"\"\naction_value_type = 2\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/top_down_shooter/mapping_contexts/switch_to_keyboard_and_mouse.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://bnd2jguy7tfti\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_gxqso\"]\n\n[resource]\nscript = ExtResource(\"1_gxqso\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/top_down_shooter/player/mrg0000.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://c22k1y6rbntlw\"\npath=\"res://.godot/imported/mrg0000.png-2c8ef2c24386191b7c1a03703c595faf.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://guide_examples/top_down_shooter/player/mrg0000.png\"\ndest_files=[\"res://.godot/imported/mrg0000.png-2c8ef2c24386191b7c1a03703c595faf.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "guide_examples/top_down_shooter/player/player.gd",
    "content": "extends CharacterBody2D\n\n\n@export var speed:float = 300\n@export var look_relative:GUIDEAction\n@export var look_absolute:GUIDEAction\n@export var move:GUIDEAction\n@export var fire:GUIDEAction\n\n@export var bolt:PackedScene\n\n@onready var left_hand:Node2D = %LeftHand\n@onready var right_hand:Node2D = %RightHand\n\n\nfunc _ready() -> void:\n\t# fire some bolts when the fire action triggers\n\tfire.triggered.connect(_fire)\n\nfunc _physics_process(delta:float) -> void:\n\tvar target := Vector2.INF\n\t\n\t# Looking at absolute coordinates. This is the case when we use a mouse.\n\tif look_absolute.is_triggered():\n\t\ttarget = look_absolute.value_axis_2d\n\t# Looking at relative coordinates. This is the case when we use a controller\t\n\telif look_relative.is_triggered():\n\t\ttarget = global_position + look_relative.value_axis_2d\n\t\n\t# If we have a target, rotate towards it\n\tif target.is_finite():\n\t\tvar target_orientation := Transform2D()\\\n\t\t\t.translated(transform.origin)\\\n\t\t\t.looking_at(target)\n\t\ttransform = transform.interpolate_with(target_orientation, 5 * delta)\n\n\t# and move according to the input. \n\tvelocity = speed * move.value_axis_2d\n\tmove_and_slide() \n\nfunc _fire() -> void:\n\t# for each hand of the player, spawn a bolt\n\tfor hand:Node2D in [left_hand, right_hand]:\n\t\tvar a_bolt:Node2D = bolt.instantiate()\n\t\tget_parent().add_child(a_bolt)\n\t\ta_bolt.global_transform = hand.global_transform\n"
  },
  {
    "path": "guide_examples/top_down_shooter/player/player.gd.uid",
    "content": "uid://blx1seoclt48w\n"
  },
  {
    "path": "guide_examples/top_down_shooter/top_down_shooter.gd",
    "content": "extends Node2D\n\n@export var keyboard_and_mouse:GUIDEMappingContext\n@export var controller:GUIDEMappingContext\n\n@export var switch_to_controller:GUIDEAction\n@export var switch_to_keyboard_and_mouse:GUIDEAction\n\nfunc _ready() -> void:\n\t# enable controller at the start\n\tGUIDE.enable_mapping_context(controller)\n\t\n\t# Switch the control scheme depending on the input.\n\tswitch_to_controller.triggered \\\n\t\t.connect(func() -> void: GUIDE.enable_mapping_context(controller, true))\n\tswitch_to_keyboard_and_mouse.triggered \\\n\t\t.connect(func() -> void: GUIDE.enable_mapping_context(keyboard_and_mouse, true))\n\t\n\n\n"
  },
  {
    "path": "guide_examples/top_down_shooter/top_down_shooter.gd.uid",
    "content": "uid://sh4xv5urdau8\n"
  },
  {
    "path": "guide_examples/top_down_shooter/top_down_shooter.tscn",
    "content": "[gd_scene load_steps=18 format=3 uid=\"uid://kmamxwy5rcyc\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/top_down_shooter/top_down_shooter.gd\" id=\"1_gvlv5\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/top_down_shooter/player/player.gd\" id=\"1_pm1t3\"]\n[ext_resource type=\"Resource\" uid=\"uid://dhp7nup127wxw\" path=\"res://guide_examples/top_down_shooter/mapping_contexts/keyboard_and_mouse.tres\" id=\"2_bl5ot\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://c22k1y6rbntlw\" path=\"res://guide_examples/top_down_shooter/player/mrg0000.png\" id=\"2_rc4yh\"]\n[ext_resource type=\"Resource\" uid=\"uid://dysrgn1ubf15g\" path=\"res://guide_examples/top_down_shooter/mapping_contexts/controller.tres\" id=\"3_tldv6\"]\n[ext_resource type=\"Resource\" uid=\"uid://byu565ktximg2\" path=\"res://guide_examples/top_down_shooter/mapping_contexts/switch_to_controller.tres\" id=\"4_rfvaw\"]\n[ext_resource type=\"Resource\" uid=\"uid://vpsh1myp67ws\" path=\"res://guide_examples/top_down_shooter/mapping_contexts/look_relative.tres\" id=\"5_hxqcn\"]\n[ext_resource type=\"Resource\" uid=\"uid://bnd2jguy7tfti\" path=\"res://guide_examples/top_down_shooter/mapping_contexts/switch_to_keyboard_and_mouse.tres\" id=\"5_usevy\"]\n[ext_resource type=\"Resource\" uid=\"uid://c4tpipfhludsi\" path=\"res://guide_examples/top_down_shooter/mapping_contexts/look_absolute.tres\" id=\"6_viqho\"]\n[ext_resource type=\"Resource\" uid=\"uid://ch070wegl722t\" path=\"res://guide_examples/top_down_shooter/mapping_contexts/move.tres\" id=\"7_gtewy\"]\n[ext_resource type=\"Resource\" uid=\"uid://coktqyup12g3w\" path=\"res://guide_examples/top_down_shooter/mapping_contexts/fire.tres\" id=\"8_kmeb0\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://dkr80d2pi0d41\" path=\"res://addons/guide/debugger/guide_debugger.tscn\" id=\"9_kqgcv\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://c0fqv8e3tljmp\" path=\"res://guide_examples/top_down_shooter/bolt/bolt.tscn\" id=\"9_ybbsa\"]\n[ext_resource type=\"Theme\" uid=\"uid://dot0gi1yoqmrl\" path=\"res://guide_examples/shared/ui_theme.tres\" id=\"14_ipln3\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/shared/instructions_label.gd\" id=\"15_d8ctw\"]\n\n[sub_resource type=\"CircleShape2D\" id=\"CircleShape2D_jh0ar\"]\nradius = 118.54\n\n[sub_resource type=\"WorldBoundaryShape2D\" id=\"WorldBoundaryShape2D_duktp\"]\n\n[node name=\"TopDownShooter\" type=\"Node2D\"]\nscript = ExtResource(\"1_gvlv5\")\nkeyboard_and_mouse = ExtResource(\"2_bl5ot\")\ncontroller = ExtResource(\"3_tldv6\")\nswitch_to_controller = ExtResource(\"4_rfvaw\")\nswitch_to_keyboard_and_mouse = ExtResource(\"5_usevy\")\n\n[node name=\"Player\" type=\"CharacterBody2D\" parent=\".\"]\nposition = Vector2(911, 479)\nmotion_mode = 1\nscript = ExtResource(\"1_pm1t3\")\nlook_relative = ExtResource(\"5_hxqcn\")\nlook_absolute = ExtResource(\"6_viqho\")\nmove = ExtResource(\"7_gtewy\")\nfire = ExtResource(\"8_kmeb0\")\nbolt = ExtResource(\"9_ybbsa\")\n\n[node name=\"Mrg0000\" type=\"Sprite2D\" parent=\"Player\"]\ntexture = ExtResource(\"2_rc4yh\")\nmetadata/_edit_lock_ = true\n\n[node name=\"CollisionShape2D\" type=\"CollisionShape2D\" parent=\"Player\"]\nshape = SubResource(\"CircleShape2D_jh0ar\")\nmetadata/_edit_lock_ = true\n\n[node name=\"LeftHand\" type=\"Marker2D\" parent=\"Player\"]\nunique_name_in_owner = true\nposition = Vector2(41, -105)\nmetadata/_edit_lock_ = true\n\n[node name=\"RightHand\" type=\"Marker2D\" parent=\"Player\"]\nunique_name_in_owner = true\nposition = Vector2(41, 109)\nmetadata/_edit_lock_ = true\n\n[node name=\"Boundary\" type=\"StaticBody2D\" parent=\".\"]\n\n[node name=\"CollisionShape2D\" type=\"CollisionShape2D\" parent=\"Boundary\"]\nposition = Vector2(831, 1077)\nshape = SubResource(\"WorldBoundaryShape2D_duktp\")\n\n[node name=\"CollisionShape2D2\" type=\"CollisionShape2D\" parent=\"Boundary\"]\nposition = Vector2(950, 1)\nrotation = 3.14159\nshape = SubResource(\"WorldBoundaryShape2D_duktp\")\n\n[node name=\"CollisionShape2D3\" type=\"CollisionShape2D\" parent=\"Boundary\"]\nposition = Vector2(1919, 523)\nrotation = 4.71239\nshape = SubResource(\"WorldBoundaryShape2D_duktp\")\n\n[node name=\"CollisionShape2D4\" type=\"CollisionShape2D\" parent=\"Boundary\"]\nposition = Vector2(-1, 530)\nrotation = 1.5708\nshape = SubResource(\"WorldBoundaryShape2D_duktp\")\n\n[node name=\"CanvasLayer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"GuideDebugger\" parent=\"CanvasLayer\" instance=ExtResource(\"9_kqgcv\")]\ntheme = ExtResource(\"14_ipln3\")\n\n[node name=\"BG\" type=\"CanvasLayer\" parent=\".\"]\nlayer = -1\n\n[node name=\"ColorRect\" type=\"ColorRect\" parent=\"BG\"]\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\nmouse_filter = 2\ncolor = Color(0.0869374, 0.147497, 0.00609748, 1)\n\n[node name=\"UI Layer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"Instructions Label Controller\" type=\"RichTextLabel\" parent=\"UI Layer\"]\nanchors_preset = 1\nanchor_left = 1.0\nanchor_right = 1.0\noffset_left = -352.0\noffset_top = 15.0\noffset_right = -105.0\noffset_bottom = 38.0\ngrow_horizontal = 0\ntheme = ExtResource(\"14_ipln3\")\nscript = ExtResource(\"15_d8ctw\")\ninstructions_text = \"Look around with %s.\nMove with %s.\nFire with %s.\n\nPress any key or move the mouse to switch to \nkeyboard and mouse.\"\nactions = Array[Resource(\"res://addons/guide/guide_action.gd\")]([ExtResource(\"5_hxqcn\"), ExtResource(\"7_gtewy\"), ExtResource(\"8_kmeb0\")])\nlimit_to_context = ExtResource(\"3_tldv6\")\nmetadata/_edit_use_anchors_ = true\n\n[node name=\"Instructions Label Keyboard and Mouse\" type=\"RichTextLabel\" parent=\"UI Layer\"]\nanchors_preset = 1\nanchor_left = 1.0\nanchor_right = 1.0\noffset_left = -352.0\noffset_top = 15.0\noffset_right = -105.0\noffset_bottom = 38.0\ngrow_horizontal = 0\ntheme = ExtResource(\"14_ipln3\")\nscript = ExtResource(\"15_d8ctw\")\ninstructions_text = \"Look around with %s.\nMove with %s.\nFire with %s.\n\nUse any controller input to switch to controller.\"\nactions = Array[Resource(\"res://addons/guide/guide_action.gd\")]([ExtResource(\"6_viqho\"), ExtResource(\"7_gtewy\"), ExtResource(\"8_kmeb0\")])\nlimit_to_context = ExtResource(\"2_bl5ot\")\nmetadata/_edit_use_anchors_ = true\n"
  },
  {
    "path": "guide_examples/touch/background.gd",
    "content": "## This just keeps the sprite endlessly scrolling. It's not related to input.\nextends Sprite2D\n\n\nfunc _process(_delta:float) -> void:\n\t# get rect of visible screen in world coordinates\n\tvar rect := get_viewport().canvas_transform.affine_inverse() * get_viewport_rect()\n\t# fit the bg into the viewport\n\tglobal_position = rect.position\n\tglobal_scale =  rect.size / texture.get_size()\n\t\n\t# update scaling so the texture scales according to zoom level\n\tmaterial.set_shader_parameter(\"scale\", global_scale)\n\tvar shader_offset :=  rect.position / texture.get_size()\n\t# and offset so we pick a texture offset relative to the movement of the camera\n\tmaterial.set_shader_parameter(\"offset\", shader_offset)\n"
  },
  {
    "path": "guide_examples/touch/background.gd.uid",
    "content": "uid://o678yabwitsl\n"
  },
  {
    "path": "guide_examples/touch/background.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://jveia1jgm1x5\"\npath=\"res://.godot/imported/background.svg-fba6babf7434090927a142c997b03f8a.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://guide_examples/touch/background.svg\"\ndest_files=[\"res://.godot/imported/background.svg-fba6babf7434090927a142c997b03f8a.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=false\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "guide_examples/touch/camera_2d.gd",
    "content": "## Camera control. We listen to GUIDE's actions to move and zoom the camera. Note how we can\n## mix event-based and polling based input handling, depending on what works better for the \n## use case.\nextends Camera2D\n\n\n@export var camera_movement:GUIDEAction\n@export var camera_zoom:GUIDEAction\n@export var camera_rotation:GUIDEAction\n@export var camera_reset:GUIDEAction\n\n\n@onready var _reference_zoom:Vector2 = zoom\n@onready var _reference_rotation:float = rotation\n\nfunc _ready() -> void:\n\tcamera_zoom.triggered.connect(_zoom_camera)\n\tcamera_rotation.triggered.connect(_rotate_camera)\n\tcamera_reset.triggered.connect(_reset_camera)\n\t# whenever zooming completes, we store the new reference zoom\n\tcamera_zoom.completed.connect(func() -> void: _reference_zoom = zoom)\n\t# whenever rotation completes, we store the new reference rotation\n\tcamera_rotation.completed.connect(func() -> void: _reference_rotation = rotation)\n\t\n\t\n\nfunc _process(_delta:float) -> void:\n\tposition += camera_movement.value_axis_2d\n\n\t\nfunc _zoom_camera() -> void:\n\tzoom = clamp( _reference_zoom * camera_zoom.value_axis_1d, Vector2(0.1, 0.1), Vector2(3, 3))\n\nfunc _rotate_camera() -> void:\n\trotation = fmod(_reference_rotation + camera_rotation.value_axis_1d, TAU)\n\n\nfunc _reset_camera() -> void:\n\tzoom = Vector2.ONE\n\trotation = 0\n\t_reference_zoom = zoom\n\t_reference_rotation = rotation\n"
  },
  {
    "path": "guide_examples/touch/camera_2d.gd.uid",
    "content": "uid://bcr7lx14f4a7o\n"
  },
  {
    "path": "guide_examples/touch/godot_head.gd",
    "content": "extends Node2D\n\n@export var lifetime_seconds:float = 5.0\nvar _remaining_time_seconds:float = 0\n\nfunc _ready() -> void:\n\t_remaining_time_seconds = lifetime_seconds\n\nfunc _process(delta:float) -> void:\n\t_remaining_time_seconds -= delta\n\tif _remaining_time_seconds <= 0:\n\t\tqueue_free()\n\t\treturn\n\t\n\tmodulate.a = _remaining_time_seconds / lifetime_seconds\n"
  },
  {
    "path": "guide_examples/touch/godot_head.gd.uid",
    "content": "uid://cpb72t1olm3ug\n"
  },
  {
    "path": "guide_examples/touch/godot_head.tscn",
    "content": "[gd_scene load_steps=3 format=3 uid=\"uid://c3kfkmt7p66c2\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/mouse_position_2d/godot_head.gd\" id=\"1_1ibdt\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://byjxtsekdl8t2\" path=\"res://guide_examples/shared/godot_logo.svg\" id=\"2_8cdku\"]\n\n[node name=\"GodotHead\" type=\"Node2D\"]\nscript = ExtResource(\"1_1ibdt\")\n\n[node name=\"Sprite2D\" type=\"Sprite2D\" parent=\".\"]\ntexture = ExtResource(\"2_8cdku\")\n"
  },
  {
    "path": "guide_examples/touch/mapping_contexts/actions/camera_movement.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://brsk1axa7e3h\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_j5nuk\"]\n\n[resource]\nscript = ExtResource(\"1_j5nuk\")\nname = &\"\"\naction_value_type = 2\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/touch/mapping_contexts/actions/camera_reset.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://dhtj0p55ylhcu\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_x2v6j\"]\n\n[resource]\nscript = ExtResource(\"1_x2v6j\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/touch/mapping_contexts/actions/camera_rotation.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://dpu1f4xeigqdr\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_a2xs0\"]\n\n[resource]\nscript = ExtResource(\"1_a2xs0\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/touch/mapping_contexts/actions/camera_zoom.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://b2xbr2rqob6gw\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_s4uaa\"]\n\n[resource]\nscript = ExtResource(\"1_s4uaa\")\nname = &\"\"\naction_value_type = 1\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/touch/mapping_contexts/actions/spawn.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://iilpc2tjr5mx\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_4jgns\"]\n\n[resource]\nscript = ExtResource(\"1_4jgns\")\nname = &\"\"\naction_value_type = 2\nblock_lower_priority_actions = false\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/touch/mapping_contexts/modifiers/zoom_sensitivity.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEModifierScale\" load_steps=2 format=3 uid=\"uid://x0g11r4xtmcv\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_scale.gd\" id=\"1_42gxx\"]\n\n[resource]\nscript = ExtResource(\"1_42gxx\")\nscale = Vector3(0.1, 1, 1)\napply_delta_time = false\n"
  },
  {
    "path": "guide_examples/touch/mapping_contexts/touch.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMappingContext\" load_steps=41 format=3 uid=\"uid://bcepjnqawyxeb\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_touch_position.gd\" id=\"2_r7fg4\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_canvas_coordinates.gd\" id=\"3_br0pk\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_input_mapping.gd\" id=\"4_ni8em\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action_mapping.gd\" id=\"5_jvujq\"]\n[ext_resource type=\"Resource\" uid=\"uid://brsk1axa7e3h\" path=\"res://guide_examples/touch/mapping_contexts/actions/camera_movement.tres\" id=\"6_5nijj\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_touch_axis_2d.gd\" id=\"7_1c4fi\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_negate.gd\" id=\"8_kce4k\"]\n[ext_resource type=\"Resource\" uid=\"uid://b2xbr2rqob6gw\" path=\"res://guide_examples/touch/mapping_contexts/actions/camera_zoom.tres\" id=\"10_s0k2q\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_touch_distance.gd\" id=\"10_xxwru\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_down.gd\" id=\"11_q5ylu\"]\n[ext_resource type=\"Resource\" uid=\"uid://iilpc2tjr5mx\" path=\"res://guide_examples/touch/mapping_contexts/actions/spawn.tres\" id=\"13_6meol\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_stability.gd\" id=\"14_iw0b0\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_mapping_context.gd\" id=\"15_1y05x\"]\n[ext_resource type=\"Resource\" uid=\"uid://dpu1f4xeigqdr\" path=\"res://guide_examples/touch/mapping_contexts/actions/camera_rotation.tres\" id=\"15_2s3pg\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_hold.gd\" id=\"15_qneu5\"]\n[ext_resource type=\"Resource\" uid=\"uid://dhtj0p55ylhcu\" path=\"res://guide_examples/touch/mapping_contexts/actions/camera_reset.tres\" id=\"16_r67n2\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_touch_angle.gd\" id=\"16_xv1hs\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_tap.gd\" id=\"17_h6kd2\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_pajkc\"]\nscript = ExtResource(\"7_1c4fi\")\nfinger_count = 1\nfinger_index = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_citsh\"]\nscript = ExtResource(\"8_kce4k\")\nx = true\ny = true\nz = true\n\n[sub_resource type=\"Resource\" id=\"Resource_ojjbt\"]\nscript = ExtResource(\"3_br0pk\")\nrelative_input = true\n\n[sub_resource type=\"Resource\" id=\"Resource_fagqu\"]\nscript = ExtResource(\"4_ni8em\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_pajkc\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_citsh\"), SubResource(\"Resource_ojjbt\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_hvdyr\"]\nscript = ExtResource(\"5_jvujq\")\naction = ExtResource(\"6_5nijj\")\ninput_mappings = Array[ExtResource(\"4_ni8em\")]([SubResource(\"Resource_fagqu\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_6jr42\"]\nscript = ExtResource(\"10_xxwru\")\n\n[sub_resource type=\"Resource\" id=\"Resource_oysb7\"]\nscript = ExtResource(\"11_q5ylu\")\nactuation_threshold = 0.0\n\n[sub_resource type=\"Resource\" id=\"Resource_drjxq\"]\nscript = ExtResource(\"4_ni8em\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_6jr42\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_oysb7\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_otc05\"]\nscript = ExtResource(\"5_jvujq\")\naction = ExtResource(\"10_s0k2q\")\ninput_mappings = Array[ExtResource(\"4_ni8em\")]([SubResource(\"Resource_drjxq\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_7c46x\"]\nscript = ExtResource(\"2_r7fg4\")\nfinger_count = 1\nfinger_index = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_l3wvc\"]\nscript = ExtResource(\"3_br0pk\")\nrelative_input = false\n\n[sub_resource type=\"Resource\" id=\"Resource_o1rij\"]\nscript = ExtResource(\"14_iw0b0\")\nmax_deviation = 1.0\ntrigger_when = 0\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_i8pe1\"]\nscript = ExtResource(\"15_qneu5\")\nhold_treshold = 1.0\nis_one_shot = true\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_8n6aa\"]\nscript = ExtResource(\"4_ni8em\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_7c46x\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_l3wvc\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_o1rij\"), SubResource(\"Resource_i8pe1\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_iivaa\"]\nscript = ExtResource(\"5_jvujq\")\naction = ExtResource(\"13_6meol\")\ninput_mappings = Array[ExtResource(\"4_ni8em\")]([SubResource(\"Resource_8n6aa\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_p0nkc\"]\nscript = ExtResource(\"16_xv1hs\")\nunit = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_6twam\"]\nscript = ExtResource(\"4_ni8em\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_p0nkc\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_tt5mo\"]\nscript = ExtResource(\"5_jvujq\")\naction = ExtResource(\"15_2s3pg\")\ninput_mappings = Array[ExtResource(\"4_ni8em\")]([SubResource(\"Resource_6twam\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_4nu5w\"]\nscript = ExtResource(\"2_r7fg4\")\nfinger_count = 3\nfinger_index = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_rvgtf\"]\nscript = ExtResource(\"17_h6kd2\")\ntap_threshold = 0.2\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_lpakr\"]\nscript = ExtResource(\"4_ni8em\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_4nu5w\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_rvgtf\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_vikxq\"]\nscript = ExtResource(\"5_jvujq\")\naction = ExtResource(\"16_r67n2\")\ninput_mappings = Array[ExtResource(\"4_ni8em\")]([SubResource(\"Resource_lpakr\")])\n\n[resource]\nscript = ExtResource(\"15_1y05x\")\ndisplay_name = \"\"\nmappings = Array[ExtResource(\"5_jvujq\")]([SubResource(\"Resource_hvdyr\"), SubResource(\"Resource_otc05\"), SubResource(\"Resource_iivaa\"), SubResource(\"Resource_tt5mo\"), SubResource(\"Resource_vikxq\")])\n"
  },
  {
    "path": "guide_examples/touch/placement_indicator/placement_indicator.gd",
    "content": "# This component shows a progress bar for the hold time, indicating to the player \n# that they must keep touching the screen until something is placed. \nextends Node2D\n\n@export var spawn:GUIDEAction\n@onready var texture_progress_bar:TextureProgressBar = %TextureProgressBar\n\nfunc _ready() -> void:\n\tvisible = false\n\t# While the hold trigger is evaluating show the progress bar\n\tspawn.ongoing.connect(_show)\n\t# Once it is done, hide it again\n\tspawn.triggered.connect(_hide)\n\t# Same when it was cancelled\n\tspawn.cancelled.connect(_hide)\n\t\nfunc _show() -> void:\n\t# show the indicator\n\tvisible = true\n\t# move it to where we would spawn\n\tglobal_position = spawn.value_axis_2d\n\t# and update the progress bar\n\ttexture_progress_bar.value = spawn.elapsed_seconds\n\t\nfunc _hide() -> void:\n\tvisible = false\n"
  },
  {
    "path": "guide_examples/touch/placement_indicator/placement_indicator.gd.uid",
    "content": "uid://cnb3sumemposh\n"
  },
  {
    "path": "guide_examples/touch/placement_indicator/placement_indicator.tscn",
    "content": "[gd_scene load_steps=4 format=3 uid=\"uid://c1ht6xduduxri\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/touch/placement_indicator/placement_indicator.gd\" id=\"1_gnpc8\"]\n[ext_resource type=\"Resource\" uid=\"uid://iilpc2tjr5mx\" path=\"res://guide_examples/touch/mapping_contexts/actions/spawn.tres\" id=\"2_grp35\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://geq3rs2fxqs4\" path=\"res://guide_examples/touch/placement_indicator/radial_progress.png\" id=\"2_slqs6\"]\n\n[node name=\"PlacementIndicator\" type=\"Node2D\"]\nscript = ExtResource(\"1_gnpc8\")\nspawn = ExtResource(\"2_grp35\")\n\n[node name=\"TextureProgressBar\" type=\"TextureProgressBar\" parent=\".\"]\nunique_name_in_owner = true\nanchors_preset = 8\nanchor_left = 0.5\nanchor_top = 0.5\nanchor_right = 0.5\nanchor_bottom = 0.5\noffset_left = -96.0\noffset_top = -96.0\noffset_right = 96.0\noffset_bottom = 96.0\ngrow_horizontal = 2\ngrow_vertical = 2\nmax_value = 1.0\nstep = 0.0\nfill_mode = 4\ntexture_progress = ExtResource(\"2_slqs6\")\n"
  },
  {
    "path": "guide_examples/touch/placement_indicator/radial_progress.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://geq3rs2fxqs4\"\npath=\"res://.godot/imported/radial_progress.png-a1f0b05f25628c67f45ec0be27847631.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://guide_examples/touch/placement_indicator/radial_progress.png\"\ndest_files=[\"res://.godot/imported/radial_progress.png-a1f0b05f25628c67f45ec0be27847631.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "guide_examples/touch/touch.gd",
    "content": "## This example shows how to use touch gestures on mobile.\nextends Node2D\n\n\n@export var mapping_context:GUIDEMappingContext\n@export var spawn:GUIDEAction\n\n@export var godot_head_scene:PackedScene\n\nfunc _ready() -> void:\n\tGUIDE.enable_mapping_context(mapping_context)\n\tspawn.triggered.connect(_spawn_godot_head)\n\n\nfunc _spawn_godot_head() -> void:\n\t# Gets the mouse cursor from G.U.I.D.E. Note how the Canvas Coordinates\n\t# modifier automatically gives us touch coordinates in canvas space\n\t# which means we don't need to take into acount the camera panning and \n\t# zoom level and can just use the coordinates we get to directly place\n\t# a Godot head at the cursor position. \n\tvar head:Node = godot_head_scene.instantiate()\n\tadd_child(head)\n\t\n\thead.global_position = spawn.value_axis_2d\n"
  },
  {
    "path": "guide_examples/touch/touch.gd.uid",
    "content": "uid://b1kmuvj74kgk8\n"
  },
  {
    "path": "guide_examples/touch/touch.tscn",
    "content": "[gd_scene load_steps=18 format=3 uid=\"uid://hxx7y33rfrnh\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/touch/touch.gd\" id=\"1_gq4pr\"]\n[ext_resource type=\"Resource\" uid=\"uid://bcepjnqawyxeb\" path=\"res://guide_examples/touch/mapping_contexts/touch.tres\" id=\"2_btyg8\"]\n[ext_resource type=\"Resource\" uid=\"uid://iilpc2tjr5mx\" path=\"res://guide_examples/touch/mapping_contexts/actions/spawn.tres\" id=\"3_nwdqb\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/touch/camera_2d.gd\" id=\"5_k0qyy\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://c3kfkmt7p66c2\" path=\"res://guide_examples/touch/godot_head.tscn\" id=\"5_mimge\"]\n[ext_resource type=\"Resource\" uid=\"uid://brsk1axa7e3h\" path=\"res://guide_examples/touch/mapping_contexts/actions/camera_movement.tres\" id=\"7_hbc7d\"]\n[ext_resource type=\"Resource\" uid=\"uid://b2xbr2rqob6gw\" path=\"res://guide_examples/touch/mapping_contexts/actions/camera_zoom.tres\" id=\"8_t7xto\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://slnmn5k0drdb\" path=\"res://guide_examples/mouse_position_2d/background.svg\" id=\"9_0mhj2\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/mouse_position_2d/background.gd\" id=\"10_7apxo\"]\n[ext_resource type=\"Theme\" uid=\"uid://dot0gi1yoqmrl\" path=\"res://guide_examples/shared/ui_theme.tres\" id=\"11_bclaf\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://c1ht6xduduxri\" path=\"res://guide_examples/touch/placement_indicator/placement_indicator.tscn\" id=\"12_e3xb3\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/shared/instructions_label.gd\" id=\"12_fwxrg\"]\n[ext_resource type=\"Resource\" uid=\"uid://dpu1f4xeigqdr\" path=\"res://guide_examples/touch/mapping_contexts/actions/camera_rotation.tres\" id=\"13_lmkyb\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://dkr80d2pi0d41\" path=\"res://addons/guide/debugger/guide_debugger.tscn\" id=\"13_n0vwi\"]\n[ext_resource type=\"Resource\" uid=\"uid://dhtj0p55ylhcu\" path=\"res://guide_examples/touch/mapping_contexts/actions/camera_reset.tres\" id=\"14_bfrfr\"]\n\n[sub_resource type=\"Shader\" id=\"Shader_v4pj1\"]\ncode = \"shader_type canvas_item;\n\nuniform vec2 scale;\nuniform vec2 offset;\n\nvoid vertex() {\n\tUV =  UV * scale + offset;\n}\n\n\n//void light() {\n\t// Called for every pixel for every light affecting the CanvasItem.\n\t// Uncomment to replace the default light processing function with this one.\n//}\n\"\n\n[sub_resource type=\"ShaderMaterial\" id=\"ShaderMaterial_1sa2x\"]\nshader = SubResource(\"Shader_v4pj1\")\nshader_parameter/scale = Vector2(1, 1)\nshader_parameter/offset = Vector2(0, 0)\n\n[node name=\"Touch\" type=\"Node2D\"]\nscript = ExtResource(\"1_gq4pr\")\nmapping_context = ExtResource(\"2_btyg8\")\nspawn = ExtResource(\"3_nwdqb\")\ngodot_head_scene = ExtResource(\"5_mimge\")\n\n[node name=\"Camera2D\" type=\"Camera2D\" parent=\".\"]\nignore_rotation = false\nscript = ExtResource(\"5_k0qyy\")\ncamera_movement = ExtResource(\"7_hbc7d\")\ncamera_zoom = ExtResource(\"8_t7xto\")\ncamera_rotation = ExtResource(\"13_lmkyb\")\ncamera_reset = ExtResource(\"14_bfrfr\")\n\n[node name=\"BG\" type=\"Sprite2D\" parent=\".\"]\ntexture_repeat = 2\nmaterial = SubResource(\"ShaderMaterial_1sa2x\")\ntexture = ExtResource(\"9_0mhj2\")\ncentered = false\nscript = ExtResource(\"10_7apxo\")\n\n[node name=\"PlacementIndicator\" parent=\".\" instance=ExtResource(\"12_e3xb3\")]\n\n[node name=\"UILayer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"RichTextLabel\" type=\"RichTextLabel\" parent=\"UILayer\"]\nanchors_preset = 1\nanchor_left = 1.0\nanchor_right = 1.0\noffset_left = -107.0\noffset_top = 41.0\noffset_right = -67.0\noffset_bottom = 81.0\ngrow_horizontal = 0\ntheme = ExtResource(\"11_bclaf\")\nscript = ExtResource(\"12_fwxrg\")\ninstructions_text = \"%s to move the camera.\n%s to zoom the camera.\n%s to rotate the camera.\n%s to reset the camera zoom and rotation.\n%s to place a Godot head.\n\"\nactions = Array[Resource(\"res://addons/guide/guide_action.gd\")]([ExtResource(\"7_hbc7d\"), ExtResource(\"8_t7xto\"), ExtResource(\"13_lmkyb\"), ExtResource(\"14_bfrfr\"), ExtResource(\"3_nwdqb\")])\n\n[node name=\"DebuggerLayer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"GuideDebugger\" parent=\"DebuggerLayer\" instance=ExtResource(\"13_n0vwi\")]\ntheme = ExtResource(\"11_bclaf\")\n"
  },
  {
    "path": "guide_examples/two_joysticks/actions/player_one_move.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://d31d5dpoavou1\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_w3ruv\"]\n\n[resource]\nscript = ExtResource(\"1_w3ruv\")\nname = &\"player_one_move\"\naction_value_type = 2\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/two_joysticks/actions/player_two_move.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://7g78pa31v44m\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_cuk0c\"]\n\n[resource]\nscript = ExtResource(\"1_cuk0c\")\nname = &\"player_two_move\"\naction_value_type = 2\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/two_joysticks/modifiers/joystick_deadzone.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEModifierDeadzone\" load_steps=2 format=3 uid=\"uid://cxd3gqa1bof30\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_deadzone.gd\" id=\"1_ax1vk\"]\n\n[resource]\nscript = ExtResource(\"1_ax1vk\")\nlower_threshold = 0.2\nupper_threshold = 1.0\n"
  },
  {
    "path": "guide_examples/two_joysticks/player.gd",
    "content": "## This is the player script. Note how we can use the same script for both\n## players, by just injecting different actions into them. No needs to check\n## which player input we should consume.\nextends Node2D\n\n@export var speed:float = 150\n\n@export var move_action:GUIDEAction\n\nfunc _process(delta:float) -> void:\n\tposition += move_action.value_axis_2d.normalized() * speed * delta\n\n"
  },
  {
    "path": "guide_examples/two_joysticks/player.gd.uid",
    "content": "uid://c73evv6t30of\n"
  },
  {
    "path": "guide_examples/two_joysticks/two_joysticks.gd",
    "content": "extends Node2D\n\n@export var mapping_context:GUIDEMappingContext\n\nfunc _ready() -> void:\n\tGUIDE.enable_mapping_context(mapping_context)\n"
  },
  {
    "path": "guide_examples/two_joysticks/two_joysticks.gd.uid",
    "content": "uid://byg4578fnips6\n"
  },
  {
    "path": "guide_examples/two_joysticks/two_joysticks.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMappingContext\" load_steps=14 format=3 uid=\"uid://chr8ugns0fh70\"]\n\n[ext_resource type=\"Resource\" uid=\"uid://d31d5dpoavou1\" path=\"res://guide_examples/two_joysticks/actions/player_one_move.tres\" id=\"1_d7e45\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_mapping_context.gd\" id=\"1_sp28b\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_joy_axis_2d.gd\" id=\"2_exwu1\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_input_mapping.gd\" id=\"3_ga6gt\"]\n[ext_resource type=\"Resource\" uid=\"uid://cxd3gqa1bof30\" path=\"res://guide_examples/two_joysticks/modifiers/joystick_deadzone.tres\" id=\"3_xy1jy\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action_mapping.gd\" id=\"4_mvoug\"]\n[ext_resource type=\"Resource\" uid=\"uid://7g78pa31v44m\" path=\"res://guide_examples/two_joysticks/actions/player_two_move.tres\" id=\"5_aeoun\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_jkkrr\"]\nscript = ExtResource(\"2_exwu1\")\nx = 0\ny = 1\njoy_index = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_6efv4\"]\nscript = ExtResource(\"3_ga6gt\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_jkkrr\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([ExtResource(\"3_xy1jy\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_kv6y3\"]\nscript = ExtResource(\"4_mvoug\")\naction = ExtResource(\"1_d7e45\")\ninput_mappings = Array[ExtResource(\"3_ga6gt\")]([SubResource(\"Resource_6efv4\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_660yw\"]\nscript = ExtResource(\"2_exwu1\")\nx = 0\ny = 1\njoy_index = 1\n\n[sub_resource type=\"Resource\" id=\"Resource_kg721\"]\nscript = ExtResource(\"3_ga6gt\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_660yw\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([ExtResource(\"3_xy1jy\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_6ruab\"]\nscript = ExtResource(\"4_mvoug\")\naction = ExtResource(\"5_aeoun\")\ninput_mappings = Array[ExtResource(\"3_ga6gt\")]([SubResource(\"Resource_kg721\")])\n\n[resource]\nscript = ExtResource(\"1_sp28b\")\ndisplay_name = \"Player Input\"\nmappings = Array[ExtResource(\"4_mvoug\")]([SubResource(\"Resource_kv6y3\"), SubResource(\"Resource_6ruab\")])\n"
  },
  {
    "path": "guide_examples/two_joysticks/two_joysticks.tscn",
    "content": "[gd_scene load_steps=8 format=3 uid=\"uid://b2uycqcjf0hth\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/two_joysticks/two_joysticks.gd\" id=\"1_1dy65\"]\n[ext_resource type=\"Resource\" uid=\"uid://chr8ugns0fh70\" path=\"res://guide_examples/two_joysticks/two_joysticks.tres\" id=\"2_3p2l3\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://byjxtsekdl8t2\" path=\"res://guide_examples/shared/godot_logo.svg\" id=\"2_c65ah\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/two_joysticks/player.gd\" id=\"3_3ycuu\"]\n[ext_resource type=\"Resource\" uid=\"uid://d31d5dpoavou1\" path=\"res://guide_examples/two_joysticks/actions/player_one_move.tres\" id=\"4_f356y\"]\n[ext_resource type=\"Resource\" uid=\"uid://7g78pa31v44m\" path=\"res://guide_examples/two_joysticks/actions/player_two_move.tres\" id=\"5_700m5\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://dkr80d2pi0d41\" path=\"res://addons/guide/debugger/guide_debugger.tscn\" id=\"7_ojfv8\"]\n\n[node name=\"TwoJoysticks\" type=\"Node2D\"]\nscript = ExtResource(\"1_1dy65\")\nmapping_context = ExtResource(\"2_3p2l3\")\n\n[node name=\"Label\" type=\"Label\" parent=\".\"]\noffset_left = 1359.0\noffset_top = 13.0\noffset_right = 1909.0\noffset_bottom = 135.0\ntext = \"This demonstrates how to consume different actions in the same script. We have two players each one controlled by a joystick. Both players share a single script and just listen to different actions which are given as export parameters.\"\nautowrap_mode = 2\n\n[node name=\"Player1\" type=\"Sprite2D\" parent=\".\"]\nposition = Vector2(509, 509)\ntexture = ExtResource(\"2_c65ah\")\nscript = ExtResource(\"3_3ycuu\")\nmove_action = ExtResource(\"4_f356y\")\n\n[node name=\"Player2\" type=\"Sprite2D\" parent=\".\"]\nmodulate = Color(1, 0.533333, 1, 1)\nposition = Vector2(1315, 505)\ntexture = ExtResource(\"2_c65ah\")\nscript = ExtResource(\"3_3ycuu\")\nmove_action = ExtResource(\"5_700m5\")\n\n[node name=\"CanvasLayer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"GuideDebugger\" parent=\"CanvasLayer\" instance=ExtResource(\"7_ojfv8\")]\nmetadata/_edit_lock_ = true\n"
  },
  {
    "path": "guide_examples/virtual_cursor/mapping_contexts/actions/click.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://by0je753kdc4\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_wkwt3\"]\n\n[resource]\nscript = ExtResource(\"1_wkwt3\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/virtual_cursor/mapping_contexts/actions/cursor_2d.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://bjmdf7hpcpg6r\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_oiyis\"]\n\n[resource]\nscript = ExtResource(\"1_oiyis\")\nname = &\"\"\naction_value_type = 2\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/virtual_cursor/mapping_contexts/actions/switch_to_controller.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://cy3gl7awoigq4\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_qkjwm\"]\n\n[resource]\nscript = ExtResource(\"1_qkjwm\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/virtual_cursor/mapping_contexts/actions/switch_to_keyboard.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://bctchx4pq708u\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_o3ji2\"]\n\n[resource]\nscript = ExtResource(\"1_o3ji2\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/virtual_cursor/mapping_contexts/controller_mappings.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMappingContext\" load_steps=28 format=3 uid=\"uid://c30c7ouhojk8x\"]\n\n[ext_resource type=\"Resource\" uid=\"uid://bjmdf7hpcpg6r\" path=\"res://guide_examples/virtual_cursor/mapping_contexts/actions/cursor_2d.tres\" id=\"1_0qk2p\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_joy_axis_2d.gd\" id=\"2_gvt20\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_input_mapping.gd\" id=\"3_h0vjd\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_deadzone.gd\" id=\"3_qf5ik\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_virtual_cursor.gd\" id=\"4_7xv7o\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action_mapping.gd\" id=\"4_hdhql\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_canvas_coordinates.gd\" id=\"5_fb5b5\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_joy_button.gd\" id=\"6_6pw4s\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_pressed.gd\" id=\"7_tw8v3\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_mapping_context.gd\" id=\"8_4mw42\"]\n[ext_resource type=\"Resource\" uid=\"uid://by0je753kdc4\" path=\"res://guide_examples/virtual_cursor/mapping_contexts/actions/click.tres\" id=\"8_nulpf\"]\n[ext_resource type=\"Resource\" uid=\"uid://bctchx4pq708u\" path=\"res://guide_examples/virtual_cursor/mapping_contexts/actions/switch_to_keyboard.tres\" id=\"11_yaqtl\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_any.gd\" id=\"12_j8p48\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_51lnp\"]\nscript = ExtResource(\"2_gvt20\")\nx = 0\ny = 1\njoy_index = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_vtltt\"]\nscript = ExtResource(\"3_qf5ik\")\nlower_threshold = 0.2\nupper_threshold = 1.0\n\n[sub_resource type=\"Resource\" id=\"Resource_k5h27\"]\nscript = ExtResource(\"4_7xv7o\")\ninitial_position = Vector2(0.5, 0.5)\ninitialize_from_mouse_position = true\napply_to_mouse_position_on_deactivation = true\nspeed = Vector3(1, 1, 1)\nscreen_scale = 1\napply_delta_time = true\nscale = Vector3(1, 1, 1)\n\n[sub_resource type=\"Resource\" id=\"Resource_r6xcs\"]\nscript = ExtResource(\"5_fb5b5\")\nrelative_input = false\n\n[sub_resource type=\"Resource\" id=\"Resource_jede7\"]\nscript = ExtResource(\"3_h0vjd\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_51lnp\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_vtltt\"), SubResource(\"Resource_k5h27\"), SubResource(\"Resource_r6xcs\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_phyr3\"]\nscript = ExtResource(\"4_hdhql\")\naction = ExtResource(\"1_0qk2p\")\ninput_mappings = Array[ExtResource(\"3_h0vjd\")]([SubResource(\"Resource_jede7\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_5fidk\"]\nscript = ExtResource(\"6_6pw4s\")\nbutton = 0\njoy_index = 0\n\n[sub_resource type=\"Resource\" id=\"Resource_m13js\"]\nscript = ExtResource(\"7_tw8v3\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_mnbnv\"]\nscript = ExtResource(\"3_h0vjd\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_5fidk\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_m13js\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_7601p\"]\nscript = ExtResource(\"4_hdhql\")\naction = ExtResource(\"8_nulpf\")\ninput_mappings = Array[ExtResource(\"3_h0vjd\")]([SubResource(\"Resource_mnbnv\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_axydo\"]\nscript = ExtResource(\"12_j8p48\")\nmouse_buttons = true\nmouse_movement = true\nminimum_mouse_movement_distance = 1.0\njoy_buttons = false\njoy_axes = false\nminimum_joy_axis_actuation_strength = 0.2\nkeyboard = true\ntouch = false\nmouse = true\njoy = false\n\n[sub_resource type=\"Resource\" id=\"Resource_yna2b\"]\nscript = ExtResource(\"7_tw8v3\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_58pwh\"]\nscript = ExtResource(\"3_h0vjd\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_axydo\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_yna2b\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_ivy7u\"]\nscript = ExtResource(\"4_hdhql\")\naction = ExtResource(\"11_yaqtl\")\ninput_mappings = Array[ExtResource(\"3_h0vjd\")]([SubResource(\"Resource_58pwh\")])\n\n[resource]\nscript = ExtResource(\"8_4mw42\")\ndisplay_name = \"\"\nmappings = Array[ExtResource(\"4_hdhql\")]([SubResource(\"Resource_phyr3\"), SubResource(\"Resource_7601p\"), SubResource(\"Resource_ivy7u\")])\n"
  },
  {
    "path": "guide_examples/virtual_cursor/mapping_contexts/keyboard_mappings.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMappingContext\" load_steps=21 format=3 uid=\"uid://dbo7gukmqll0o\"]\n\n[ext_resource type=\"Resource\" uid=\"uid://bjmdf7hpcpg6r\" path=\"res://guide_examples/virtual_cursor/mapping_contexts/actions/cursor_2d.tres\" id=\"1_sjpxj\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_mouse_position.gd\" id=\"2_gng6o\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_input_mapping.gd\" id=\"5_6tvqc\"]\n[ext_resource type=\"Resource\" uid=\"uid://by0je753kdc4\" path=\"res://guide_examples/virtual_cursor/mapping_contexts/actions/click.tres\" id=\"5_xn3xn\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_mouse_button.gd\" id=\"6_c4twy\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action_mapping.gd\" id=\"6_r1gr7\"]\n[ext_resource type=\"Resource\" uid=\"uid://cy3gl7awoigq4\" path=\"res://guide_examples/virtual_cursor/mapping_contexts/actions/switch_to_controller.tres\" id=\"8_mcomh\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_any.gd\" id=\"9_0m1pn\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/triggers/guide_trigger_pressed.gd\" id=\"9_4ujfx\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_mapping_context.gd\" id=\"10_mvbc0\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_uvyp8\"]\nscript = ExtResource(\"2_gng6o\")\n\n[sub_resource type=\"Resource\" id=\"Resource_jede7\"]\nscript = ExtResource(\"5_6tvqc\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_uvyp8\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_phyr3\"]\nscript = ExtResource(\"6_r1gr7\")\naction = ExtResource(\"1_sjpxj\")\ninput_mappings = Array[ExtResource(\"5_6tvqc\")]([SubResource(\"Resource_jede7\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_g5bhn\"]\nscript = ExtResource(\"6_c4twy\")\nbutton = 1\n\n[sub_resource type=\"Resource\" id=\"Resource_m13js\"]\nscript = ExtResource(\"9_4ujfx\")\nactuation_threshold = 0.5\n\n[sub_resource type=\"Resource\" id=\"Resource_mnbnv\"]\nscript = ExtResource(\"5_6tvqc\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_g5bhn\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([SubResource(\"Resource_m13js\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_7601p\"]\nscript = ExtResource(\"6_r1gr7\")\naction = ExtResource(\"5_xn3xn\")\ninput_mappings = Array[ExtResource(\"5_6tvqc\")]([SubResource(\"Resource_mnbnv\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_rhita\"]\nscript = ExtResource(\"9_0m1pn\")\nmouse_buttons = false\nmouse_movement = false\nminimum_mouse_movement_distance = 1.0\njoy_buttons = true\njoy_axes = true\nminimum_joy_axis_actuation_strength = 0.3\nkeyboard = false\ntouch = false\nmouse = false\njoy = true\n\n[sub_resource type=\"Resource\" id=\"Resource_grun2\"]\nscript = ExtResource(\"5_6tvqc\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_rhita\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\nmetadata/_guide_triggers_collapsed = false\n\n[sub_resource type=\"Resource\" id=\"Resource_1qr0o\"]\nscript = ExtResource(\"6_r1gr7\")\naction = ExtResource(\"8_mcomh\")\ninput_mappings = Array[ExtResource(\"5_6tvqc\")]([SubResource(\"Resource_grun2\")])\n\n[resource]\nscript = ExtResource(\"10_mvbc0\")\ndisplay_name = \"\"\nmappings = Array[ExtResource(\"6_r1gr7\")]([SubResource(\"Resource_phyr3\"), SubResource(\"Resource_7601p\"), SubResource(\"Resource_1qr0o\")])\n"
  },
  {
    "path": "guide_examples/virtual_cursor/pointable/pointable.gd",
    "content": "extends Area2D\n\n\nvar _is_spinning:bool = false\n\nfunc spin() -> void:\n\tif _is_spinning:\n\t\treturn\n\t_is_spinning = true\n\tvar tween := create_tween()\n\ttween.tween_property(self, \"rotation_degrees\", 360, 0.5)\n\tawait tween.finished\n\t\n\trotation_degrees = 0\n\t_is_spinning = false\n"
  },
  {
    "path": "guide_examples/virtual_cursor/pointable/pointable.gd.uid",
    "content": "uid://dghhpx6mxb4lr\n"
  },
  {
    "path": "guide_examples/virtual_cursor/pointable/pointable.tscn",
    "content": "[gd_scene load_steps=4 format=3 uid=\"uid://c2rl058s7wq5r\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/virtual_cursor/pointable/pointable.gd\" id=\"1_dmsp0\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://byjxtsekdl8t2\" path=\"res://guide_examples/shared/godot_logo.svg\" id=\"2_eubij\"]\n\n[sub_resource type=\"RectangleShape2D\" id=\"RectangleShape2D_cccqq\"]\nsize = Vector2(128, 128)\n\n[node name=\"Pointable\" type=\"Area2D\"]\nmonitoring = false\nscript = ExtResource(\"1_dmsp0\")\n\n[node name=\"GodotLogo\" type=\"Sprite2D\" parent=\".\"]\ntexture = ExtResource(\"2_eubij\")\n\n[node name=\"CollisionShape2D\" type=\"CollisionShape2D\" parent=\".\"]\nshape = SubResource(\"RectangleShape2D_cccqq\")\n"
  },
  {
    "path": "guide_examples/virtual_cursor/pointer.gd",
    "content": "extends Area2D\n\n@export var cursor_2d:GUIDEAction\n@export var click:GUIDEAction\n\n\nfunc _ready() -> void:\n\tclick.triggered.connect(_click)\n\nfunc _process(_delta:float) -> void:\n\tglobal_position = cursor_2d.value_axis_2d\n\n\nfunc _click() -> void:\n\tfor clickable in get_overlapping_areas():\n\t\tif clickable.has_method(\"spin\"):\n\t\t\tclickable.spin()\n"
  },
  {
    "path": "guide_examples/virtual_cursor/pointer.gd.uid",
    "content": "uid://cyhgr4oiher4l\n"
  },
  {
    "path": "guide_examples/virtual_cursor/pointer.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://ch3cxfrdxksld\"\npath=\"res://.godot/imported/pointer.svg-2068835178847b731dd1d6754048fd27.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://guide_examples/virtual_cursor/pointer.svg\"\ndest_files=[\"res://.godot/imported/pointer.svg-2068835178847b731dd1d6754048fd27.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=false\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "guide_examples/virtual_cursor/virtual_cursor.gd",
    "content": "extends Node2D\n\n@export var keyboard_and_mouse:GUIDEMappingContext\n@export var controller:GUIDEMappingContext\n\n@export var switch_to_controller:GUIDEAction\n@export var switch_to_keyboard_and_mouse:GUIDEAction\n\n\nfunc _ready() -> void:\n\tInput.mouse_mode = Input.MOUSE_MODE_HIDDEN\n\tGUIDE.enable_mapping_context(keyboard_and_mouse)\n\t\n\tswitch_to_controller.triggered.connect(_to_controller)\n\tswitch_to_keyboard_and_mouse.triggered.connect(_to_keyboard_and_mouse)\n\t\n\t\nfunc _to_controller() -> void:\n\t# call_deferred is needed because this is called from an action signal handler,\n\t# which runs during GUIDE's internal update. Changing contexts directly from\n\t# there is not allowed, similar to modifying physics state in a physics callback.\n\tGUIDE.enable_mapping_context.call_deferred(controller, true)\n\t\n\t\nfunc _to_keyboard_and_mouse() -> void:\n\tGUIDE.enable_mapping_context.call_deferred(keyboard_and_mouse, true)\n"
  },
  {
    "path": "guide_examples/virtual_cursor/virtual_cursor.gd.uid",
    "content": "uid://dvv04npvyv3mh\n"
  },
  {
    "path": "guide_examples/virtual_cursor/virtual_cursor.tscn",
    "content": "[gd_scene load_steps=14 format=3 uid=\"uid://dmsa3rdgybqdd\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/virtual_cursor/virtual_cursor.gd\" id=\"1_2rp0j\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/virtual_cursor/pointer.gd\" id=\"2_h1nim\"]\n[ext_resource type=\"Resource\" uid=\"uid://dbo7gukmqll0o\" path=\"res://guide_examples/virtual_cursor/mapping_contexts/keyboard_mappings.tres\" id=\"2_tqdti\"]\n[ext_resource type=\"Resource\" uid=\"uid://c30c7ouhojk8x\" path=\"res://guide_examples/virtual_cursor/mapping_contexts/controller_mappings.tres\" id=\"3_xcoq2\"]\n[ext_resource type=\"Resource\" uid=\"uid://cy3gl7awoigq4\" path=\"res://guide_examples/virtual_cursor/mapping_contexts/actions/switch_to_controller.tres\" id=\"4_ofxlp\"]\n[ext_resource type=\"Resource\" uid=\"uid://bctchx4pq708u\" path=\"res://guide_examples/virtual_cursor/mapping_contexts/actions/switch_to_keyboard.tres\" id=\"5_pahhr\"]\n[ext_resource type=\"Resource\" uid=\"uid://bjmdf7hpcpg6r\" path=\"res://guide_examples/virtual_cursor/mapping_contexts/actions/cursor_2d.tres\" id=\"7_18ein\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://c2rl058s7wq5r\" path=\"res://guide_examples/virtual_cursor/pointable/pointable.tscn\" id=\"7_rhw28\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://dkr80d2pi0d41\" path=\"res://addons/guide/debugger/guide_debugger.tscn\" id=\"8_f1dtb\"]\n[ext_resource type=\"Resource\" uid=\"uid://by0je753kdc4\" path=\"res://guide_examples/virtual_cursor/mapping_contexts/actions/click.tres\" id=\"8_sx06i\"]\n[ext_resource type=\"Theme\" uid=\"uid://dot0gi1yoqmrl\" path=\"res://guide_examples/shared/ui_theme.tres\" id=\"9_ra860\"]\n\n[sub_resource type=\"CompressedTexture2D\" id=\"CompressedTexture2D_7n6hb\"]\nload_path = \"res://.godot/imported/pointer.svg-2068835178847b731dd1d6754048fd27.ctex\"\n\n[sub_resource type=\"CircleShape2D\" id=\"CircleShape2D_ltt1b\"]\nradius = 32.95\n\n[node name=\"VirtualCursor\" type=\"Node2D\"]\nscript = ExtResource(\"1_2rp0j\")\nkeyboard_and_mouse = ExtResource(\"2_tqdti\")\ncontroller = ExtResource(\"3_xcoq2\")\nswitch_to_controller = ExtResource(\"4_ofxlp\")\nswitch_to_keyboard_and_mouse = ExtResource(\"5_pahhr\")\n\n[node name=\"Pointable\" parent=\".\" instance=ExtResource(\"7_rhw28\")]\nposition = Vector2(300, 271)\n\n[node name=\"Pointable2\" parent=\".\" instance=ExtResource(\"7_rhw28\")]\nposition = Vector2(1248, 311)\n\n[node name=\"Pointable3\" parent=\".\" instance=ExtResource(\"7_rhw28\")]\nposition = Vector2(672, 835)\n\n[node name=\"Pointer\" type=\"Area2D\" parent=\".\"]\nposition = Vector2(813, 485)\nscript = ExtResource(\"2_h1nim\")\ncursor_2d = ExtResource(\"7_18ein\")\nclick = ExtResource(\"8_sx06i\")\n\n[node name=\"PointerVisual\" type=\"Sprite2D\" parent=\"Pointer\"]\ntexture = SubResource(\"CompressedTexture2D_7n6hb\")\ncentered = false\n\n[node name=\"CollisionShape2D\" type=\"CollisionShape2D\" parent=\"Pointer\"]\nshape = SubResource(\"CircleShape2D_ltt1b\")\n\n[node name=\"CanvasLayer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"GuideDebugger\" parent=\"CanvasLayer\" instance=ExtResource(\"8_f1dtb\")]\ntheme = ExtResource(\"9_ra860\")\n\n[node name=\"Label\" type=\"Label\" parent=\"CanvasLayer\"]\ncustom_minimum_size = Vector2(500, 0)\nanchors_preset = 3\nanchor_left = 1.0\nanchor_top = 1.0\nanchor_right = 1.0\nanchor_bottom = 1.0\noffset_left = -100.0\noffset_top = -63.0\noffset_right = -60.0\noffset_bottom = -40.0\ngrow_horizontal = 0\ngrow_vertical = 0\ntheme_override_font_sizes/font_size = 21\ntext = \"This example shows how to use the Virtual Cursor modifier. You can move the cursor both with the mouse or with the gamepad. While the gamepad is used, the Virtual Cursor modifier is emulating the mouse position. \"\nhorizontal_alignment = 2\nautowrap_mode = 2\n"
  },
  {
    "path": "guide_examples/virtual_sticks/custom_stick_renderer/custom_stick_renderer.gd",
    "content": "## This is an example of a custom renderer for virtual sticks.\n## This renderer uses a shader to render the stick rather than predefined\n## textures. \n@tool\nextends GUIDEVirtualStickRenderer\n\n@export_range(0,1,0.001) var outline_thickness:float = 0.025:\n\tset(value):\n\t\toutline_thickness = value\n\t\t_rebuild()\n\nvar _rect:ColorRect\nvar _material:ShaderMaterial\n\nvar _multiplier:float = 1.0\n\n\nfunc _on_configuration_changed() -> void:\n\t_rebuild()\n\nfunc _rebuild() -> void:\t\n\tif not is_node_ready():\n\t\treturn\n\t\t\n\tif not is_instance_valid(_rect):\n\t\t_rect = ColorRect.new()\n\t\tget_parent().add_child(_rect)\n\t\t_material = ShaderMaterial.new()\n\t\t_material.shader = preload(\"custom_stick_renderer.gdshader\")\n\t\t_rect.material = _material\n\t\n\t# make the control big enough to render the joy fully without\n\t# cutting \n\tvar half_size := max_actuation_radius + stick_radius\n\thalf_size += half_size * outline_thickness \n\t\n\t# the multiplier tells us which fraction of the total half size is \n\t# the max actuation radius. this prevents us from moving the stick too\n\t# far.\n\t_multiplier = max_actuation_radius / half_size\n\t_rect.custom_minimum_size = Vector2(2 * half_size, 2 * half_size)\n\t\t\n\t_material.set_shader_parameter(\"stick_radius\", stick_radius / (2.0 * half_size))\n\t_material.set_shader_parameter(\"outline_thickness\", outline_thickness)\t\n\t\t\nfunc _update(_joy_position: Vector2, joy_offset:Vector2, _is_actuated:bool) -> void:\n\t_material.set_shader_parameter(\"stick_position\", Vector2(0.5, 0.5) + _multiplier * joy_offset / 2.0)\n"
  },
  {
    "path": "guide_examples/virtual_sticks/custom_stick_renderer/custom_stick_renderer.gd.uid",
    "content": "uid://ce76hf4ebt8ce\n"
  },
  {
    "path": "guide_examples/virtual_sticks/custom_stick_renderer/custom_stick_renderer.gdshader",
    "content": "shader_type canvas_item;\n\n// Radius of the stick in UV units\nuniform float stick_radius = 0.1;\n\n// Position of the stick in UV units\nuniform vec2 stick_position = vec2(0.5, 0.5);\n\n// thickness of the outline\nuniform float outline_thickness = 0.05;\n\n// SDF for a smooth union\n// https://iquilezles.org/articles/distfunctions/\nfloat smooth_union(float first, float second, float smoothness) {\n\tsmoothness *= 4.0;\n    float h = max(smoothness-abs(first-second),0.0);\n    return min(first, second) - h*h*0.25/smoothness;\t\n}\n\nfloat outline(float value, float thickness) {\n\tfloat radius = thickness / 2.0;\n\treturn -(radius-abs(value));\n}\n\n// SDF for the joy at the given position using the given \n// joy radius and joy position.\nfloat circle(vec2 position, float joy_radius, vec2 joy_position) {\n\treturn length(position-joy_position) - joy_radius; \n}\n\n\nvoid fragment() {\n\tfloat center = circle(UV, stick_radius * 0.5, vec2(0.5));\n\tfloat moved = circle(UV, stick_radius, stick_position);\n\tfloat final = outline(smooth_union(center, moved, 0.01), outline_thickness);\n\tfloat mask = clamp(-sign(final), 0, 1);\n\t\n\tCOLOR = vec4(1) * mask;\n}\n"
  },
  {
    "path": "guide_examples/virtual_sticks/custom_stick_renderer/custom_stick_renderer.gdshader.uid",
    "content": "uid://c3ur0gh0iwsub\n"
  },
  {
    "path": "guide_examples/virtual_sticks/mapping_contexts/fire_projectile.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://d5htyjdft7bsk\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_j0v6y\"]\n\n[resource]\nscript = ExtResource(\"1_j0v6y\")\nname = &\"\"\naction_value_type = 0\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/virtual_sticks/mapping_contexts/mapping_context.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEMappingContext\" load_steps=22 format=3 uid=\"uid://4bf73j4y33fd\"]\n\n[ext_resource type=\"Resource\" uid=\"uid://c36k5e3jjyp0w\" path=\"res://guide_examples/virtual_sticks/mapping_contexts/move_ship.tres\" id=\"1_e4vbv\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_joy_axis_2d.gd\" id=\"2_jhgj0\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/modifiers/guide_modifier_deadzone.gd\" id=\"3_4xnit\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_input_mapping.gd\" id=\"4_lns13\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action_mapping.gd\" id=\"5_wprvf\"]\n[ext_resource type=\"Resource\" uid=\"uid://bjac08qt2mf2s\" path=\"res://guide_examples/virtual_sticks/mapping_contexts/rotate_ship.tres\" id=\"6_44qy6\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_joy_axis_1d.gd\" id=\"7_g4bxd\"]\n[ext_resource type=\"Resource\" path=\"res://guide_examples/virtual_sticks/mapping_contexts/fire_projectile.tres\" id=\"8_fire\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_mapping_context.gd\" id=\"9_h4jvs\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/inputs/guide_input_joy_button.gd\" id=\"10_btn\"]\n\n[sub_resource type=\"Resource\" id=\"Resource_o2lml\"]\nscript = ExtResource(\"2_jhgj0\")\nx = 0\ny = 1\njoy_index = -2\n\n[sub_resource type=\"Resource\" id=\"Resource_3im2j\"]\nscript = ExtResource(\"3_4xnit\")\nlower_threshold = 0.2\nupper_threshold = 1.0\n\n[sub_resource type=\"Resource\" id=\"Resource_qlbpd\"]\nscript = ExtResource(\"4_lns13\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_o2lml\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_3im2j\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_f6h3o\"]\nscript = ExtResource(\"5_wprvf\")\naction = ExtResource(\"1_e4vbv\")\ninput_mappings = Array[ExtResource(\"4_lns13\")]([SubResource(\"Resource_qlbpd\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_puta0\"]\nscript = ExtResource(\"7_g4bxd\")\naxis = 2\njoy_index = -2\n\n[sub_resource type=\"Resource\" id=\"Resource_qt4pq\"]\nscript = ExtResource(\"3_4xnit\")\nlower_threshold = 0.2\nupper_threshold = 1.0\n\n[sub_resource type=\"Resource\" id=\"Resource_hgim7\"]\nscript = ExtResource(\"4_lns13\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_puta0\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([SubResource(\"Resource_qt4pq\")])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_n0wg6\"]\nscript = ExtResource(\"5_wprvf\")\naction = ExtResource(\"6_44qy6\")\ninput_mappings = Array[ExtResource(\"4_lns13\")]([SubResource(\"Resource_hgim7\")])\n\n[sub_resource type=\"Resource\" id=\"Resource_btn_inp\"]\nscript = ExtResource(\"10_btn\")\nbutton = 0\njoy_index = -2\n\n[sub_resource type=\"Resource\" id=\"Resource_btn_map\"]\nscript = ExtResource(\"4_lns13\")\noverride_action_settings = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\ninput = SubResource(\"Resource_btn_inp\")\nmodifiers = Array[Resource(\"res://addons/guide/modifiers/guide_modifier.gd\")]([])\ntriggers = Array[Resource(\"res://addons/guide/triggers/guide_trigger.gd\")]([])\n\n[sub_resource type=\"Resource\" id=\"Resource_fire_map\"]\nscript = ExtResource(\"5_wprvf\")\naction = ExtResource(\"8_fire\")\ninput_mappings = Array[ExtResource(\"4_lns13\")]([SubResource(\"Resource_btn_map\")])\n\n[resource]\nscript = ExtResource(\"9_h4jvs\")\ndisplay_name = \"\"\nmappings = Array[ExtResource(\"5_wprvf\")]([SubResource(\"Resource_f6h3o\"), SubResource(\"Resource_n0wg6\"), SubResource(\"Resource_fire_map\")])\n"
  },
  {
    "path": "guide_examples/virtual_sticks/mapping_contexts/move_ship.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://c36k5e3jjyp0w\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_ihfol\"]\n\n[resource]\nscript = ExtResource(\"1_ihfol\")\nname = &\"\"\naction_value_type = 2\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/virtual_sticks/mapping_contexts/rotate_ship.tres",
    "content": "[gd_resource type=\"Resource\" script_class=\"GUIDEAction\" load_steps=2 format=3 uid=\"uid://bjac08qt2mf2s\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/guide_action.gd\" id=\"1_anolp\"]\n\n[resource]\nscript = ExtResource(\"1_anolp\")\nname = &\"\"\naction_value_type = 1\nblock_lower_priority_actions = true\nemit_as_godot_actions = false\nis_remappable = false\ndisplay_name = \"\"\ndisplay_category = \"\"\n"
  },
  {
    "path": "guide_examples/virtual_sticks/projectile/laser.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://d2u5t2aypf0u\"\npath=\"res://.godot/imported/laser.svg-904a6e365e093bfe37be0635a5f0e7dd.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://guide_examples/virtual_sticks/projectile/laser.svg\"\ndest_files=[\"res://.godot/imported/laser.svg-904a6e365e093bfe37be0635a5f0e7dd.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=false\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "guide_examples/virtual_sticks/projectile/projectile.gd",
    "content": "## A fast-flying laser projectile.\n## The projectile moves straight in the provided direction and despawns after its lifetime.\n## @param speed Units per second to travel.\n## @param lifetime Seconds to live before freeing.\nclass_name LaserProjectile\nextends Node2D\n\n@export var speed: float = 900.0\n@export var lifetime: float = 1.5\n\nvar _age: float = 0.0\n\nfunc _process(delta: float) -> void:\n\ttranslate(transform.x * speed * delta)\n\t_age += delta\n\tif _age >= lifetime:\n\t\tqueue_free()\n\n"
  },
  {
    "path": "guide_examples/virtual_sticks/projectile/projectile.gd.uid",
    "content": "uid://cbxlaqfj7ehqf\n"
  },
  {
    "path": "guide_examples/virtual_sticks/projectile/projectile.tscn",
    "content": "[gd_scene load_steps=3 format=3 uid=\"uid://dd5ociyaxqae3\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/virtual_sticks/projectile/projectile.gd\" id=\"1_script\"]\n[ext_resource type=\"Texture2D\" path=\"res://guide_examples/virtual_sticks/projectile/laser.svg\" id=\"2_tex\"]\n\n[node name=\"Projectile\" type=\"Node2D\"]\nscript = ExtResource(\"1_script\")\n\n[node name=\"Laser\" type=\"Sprite2D\" parent=\".\"]\ntexture = ExtResource(\"2_tex\")\ncentered = false\n"
  },
  {
    "path": "guide_examples/virtual_sticks/ship/ship.gd",
    "content": "extends Node2D\n\n@export var move_ship: GUIDEAction\n@export var rotate_ship: GUIDEAction\n@export var fire_projectile: GUIDEAction\n@export var speed: float = 200.0\n@export var rotation_speed_degrees: float = 360.0\n@export var projectile_scene: PackedScene\n@export var projectile_spawn_offset: float = 40.0\n\nfunc _ready() -> void:\n\tfire_projectile.triggered.connect(_fire)\n\nfunc _process(delta: float) -> void:\n\trotate(delta * rotate_ship.value_axis_1d * deg_to_rad(rotation_speed_degrees))\n\tvar move: Vector2 = move_ship.value_axis_2d\n\ttranslate((transform.x * -move.y + transform.y * move.x) * speed * delta)\n\nfunc _fire() -> void:\n\tif not is_instance_valid(projectile_scene):\n\t\treturn\n\n\tvar projectile: LaserProjectile = projectile_scene.instantiate() as LaserProjectile\n\tget_parent().add_child(projectile)\n\t\n\tprojectile.global_transform = global_transform.translated(transform.x.normalized() * projectile_spawn_offset)\n\t\n"
  },
  {
    "path": "guide_examples/virtual_sticks/ship/ship.gd.uid",
    "content": "uid://cj3vsfo4g6gyl\n"
  },
  {
    "path": "guide_examples/virtual_sticks/ship/ship.tscn",
    "content": "[gd_scene load_steps=3 format=3 uid=\"uid://dnsukbw60m8xf\"]\n\n[ext_resource type=\"Script\" path=\"res://guide_examples/virtual_sticks/ship/ship.gd\" id=\"1_jxjub\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://cikl7i56dtup3\" path=\"res://guide_examples/virtual_sticks/ship/spaceship.png\" id=\"2_ubjvp\"]\n\n[node name=\"Ship\" type=\"Node2D\"]\nscript = ExtResource(\"1_jxjub\")\n\n[node name=\"Spaceship\" type=\"Sprite2D\" parent=\".\"]\nrotation = 1.5708\ntexture = ExtResource(\"2_ubjvp\")\n"
  },
  {
    "path": "guide_examples/virtual_sticks/ship/spaceship.png.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://cikl7i56dtup3\"\npath=\"res://.godot/imported/spaceship.png-131f89d523f521dbc53aa2a07f33cd1a.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://guide_examples/virtual_sticks/ship/spaceship.png\"\ndest_files=[\"res://.godot/imported/spaceship.png-131f89d523f521dbc53aa2a07f33cd1a.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\n"
  },
  {
    "path": "guide_examples/virtual_sticks/virtual_sticks.gd",
    "content": "extends Node\n\n@export var mapping_context:GUIDEMappingContext\n\n\nfunc _ready() -> void:\n\tGUIDE.enable_mapping_context(mapping_context)\n\t\n"
  },
  {
    "path": "guide_examples/virtual_sticks/virtual_sticks.gd.uid",
    "content": "uid://em5cg3v0n1qm\n"
  },
  {
    "path": "guide_examples/virtual_sticks/virtual_sticks.tscn",
    "content": "[gd_scene load_steps=15 format=3 uid=\"uid://m2d5cmtuheb\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/guide/ui/virtual_joy/virtual_stick/guide_virtual_stick.gd\" id=\"1_8y7sk\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/virtual_sticks/virtual_sticks.gd\" id=\"1_naqqt\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://dkr80d2pi0d41\" path=\"res://addons/guide/debugger/guide_debugger.tscn\" id=\"1_yjipb\"]\n[ext_resource type=\"Resource\" uid=\"uid://4bf73j4y33fd\" path=\"res://guide_examples/virtual_sticks/mapping_contexts/mapping_context.tres\" id=\"2_xu2j8\"]\n[ext_resource type=\"PackedScene\" uid=\"uid://dnsukbw60m8xf\" path=\"res://guide_examples/virtual_sticks/ship/ship.tscn\" id=\"3_scjee\"]\n[ext_resource type=\"Resource\" uid=\"uid://c36k5e3jjyp0w\" path=\"res://guide_examples/virtual_sticks/mapping_contexts/move_ship.tres\" id=\"4_1f0iq\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/ui/virtual_joy/virtual_stick/texture_renderer/guide_virtual_stick_texture_renderer.gd\" id=\"5_ejjjy\"]\n[ext_resource type=\"Resource\" uid=\"uid://bjac08qt2mf2s\" path=\"res://guide_examples/virtual_sticks/mapping_contexts/rotate_ship.tres\" id=\"5_h0yu2\"]\n[ext_resource type=\"Resource\" path=\"res://guide_examples/virtual_sticks/mapping_contexts/fire_projectile.tres\" id=\"6_j3ftr\"]\n[ext_resource type=\"Script\" path=\"res://guide_examples/virtual_sticks/custom_stick_renderer/custom_stick_renderer.gd\" id=\"8_mux4w\"]\n[ext_resource type=\"Theme\" uid=\"uid://dot0gi1yoqmrl\" path=\"res://guide_examples/shared/ui_theme.tres\" id=\"9_5tbf1\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/ui/virtual_joy/virtual_button/guide_virtual_button.gd\" id=\"9_67dlh\"]\n[ext_resource type=\"Script\" path=\"res://addons/guide/ui/virtual_joy/virtual_button/texture_renderer/guide_virtual_button_texture_renderer.gd\" id=\"11_hg28x\"]\n[ext_resource type=\"PackedScene\" path=\"res://guide_examples/virtual_sticks/projectile/projectile.tscn\" id=\"13_proj\"]\n\n[node name=\"VirtualSticks\" type=\"Node\"]\nscript = ExtResource(\"1_naqqt\")\nmapping_context = ExtResource(\"2_xu2j8\")\n\n[node name=\"ColorRect\" type=\"ColorRect\" parent=\".\"]\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\nmouse_filter = 2\ncolor = Color(0.0305977, 3.65854e-05, 0.0974683, 1)\n\n[node name=\"Ship\" parent=\".\" instance=ExtResource(\"3_scjee\")]\nposition = Vector2(867, 425)\nrotation = -1.5708\nmove_ship = ExtResource(\"4_1f0iq\")\nrotate_ship = ExtResource(\"5_h0yu2\")\nfire_projectile = ExtResource(\"6_j3ftr\")\nprojectile_scene = ExtResource(\"13_proj\")\n\n[node name=\"CanvasLayer2\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"GuideDebugger\" parent=\"CanvasLayer2\" instance=ExtResource(\"1_yjipb\")]\ntheme = ExtResource(\"9_5tbf1\")\n\n[node name=\"CanvasLayer\" type=\"CanvasLayer\" parent=\".\"]\n\n[node name=\"LeftStick\" type=\"CenterContainer\" parent=\"CanvasLayer\"]\nanchors_preset = 2\nanchor_top = 1.0\nanchor_bottom = 1.0\noffset_left = 285.0\noffset_top = -236.0\noffset_right = 285.0\noffset_bottom = -236.0\ngrow_vertical = 0\nmouse_filter = 2\nuse_top_left = true\nscript = ExtResource(\"1_8y7sk\")\nposition_mode = 1\ninput_mode = 2\n\n[node name=\"CustomStickRenderer\" type=\"Control\" parent=\"CanvasLayer/LeftStick\"]\nlayout_mode = 2\nscript = ExtResource(\"8_mux4w\")\noutline_thickness = 0.021\n\n[node name=\"RightStick\" type=\"CenterContainer\" parent=\"CanvasLayer\"]\nanchors_preset = 3\nanchor_left = 1.0\nanchor_top = 1.0\nanchor_right = 1.0\nanchor_bottom = 1.0\noffset_left = -385.0\noffset_top = -205.0\noffset_right = -385.0\noffset_bottom = -205.0\ngrow_horizontal = 0\ngrow_vertical = 0\nmouse_filter = 2\nuse_top_left = true\nscript = ExtResource(\"1_8y7sk\")\nstick_position = 1\ninput_mode = 2\n\n[node name=\"TextureStickRenderer\" type=\"Control\" parent=\"CanvasLayer/RightStick\"]\nlayout_mode = 2\nscript = ExtResource(\"5_ejjjy\")\n\n[node name=\"GUIDEVirtualButton\" type=\"CenterContainer\" parent=\"CanvasLayer\"]\nanchors_preset = 3\nanchor_left = 1.0\nanchor_top = 1.0\nanchor_right = 1.0\nanchor_bottom = 1.0\noffset_left = -157.0\noffset_top = -382.0\noffset_right = -157.0\noffset_bottom = -382.0\ngrow_horizontal = 0\ngrow_vertical = 0\nmouse_filter = 2\nuse_top_left = true\nscript = ExtResource(\"9_67dlh\")\ninput_mode = 2\n\n[node name=\"TextureButtonRenderer\" type=\"Control\" parent=\"CanvasLayer/GUIDEVirtualButton\"]\nlayout_mode = 2\nscript = ExtResource(\"11_hg28x\")\n"
  },
  {
    "path": "icon.svg.import",
    "content": "[remap]\n\nimporter=\"texture\"\ntype=\"CompressedTexture2D\"\nuid=\"uid://c07v7ndnkcqr8\"\npath=\"res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex\"\nmetadata={\n\"vram_texture\": false\n}\n\n[deps]\n\nsource_file=\"res://icon.svg\"\ndest_files=[\"res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex\"]\n\n[params]\n\ncompress/mode=0\ncompress/high_quality=false\ncompress/lossy_quality=0.7\ncompress/hdr_compression=1\ncompress/normal_map=0\ncompress/channel_pack=0\nmipmaps/generate=false\nmipmaps/limit=-1\nroughness/mode=0\nroughness/src_normal=\"\"\nprocess/fix_alpha_border=true\nprocess/premult_alpha=false\nprocess/normal_map_invert_y=false\nprocess/hdr_as_srgb=false\nprocess/hdr_clamp_exposure=false\nprocess/size_limit=0\ndetect_3d/compress_to=1\nsvg/scale=1.0\neditor/scale_with_editor_scale=false\neditor/convert_colors_with_editor_theme=false\n"
  },
  {
    "path": "project.godot",
    "content": "; Engine configuration file.\n; It's best edited using the editor UI and not directly,\n; since the parameters that go here are not all obvious.\n;\n; Format:\n;   [section] ; section goes between []\n;   param=value ; assign values to parameters\n\nconfig_version=5\n\n[Guide]\n\n\"Editor/Joy Icons\"=2\n\"Editor/Joy Type\"=1\n\n[addons]\n\ndiagnostic_list/auto_refresh=false\n\n[application]\n\nconfig/name=\"G.U.I.D.E\"\nrun/main_scene=\"res://guide_examples/virtual_sticks/virtual_sticks.tscn\"\nconfig/features=PackedStringArray(\"4.2\", \"Forward Plus\")\nconfig/icon=\"res://icon.svg\"\n\n[autoload]\n\nGUIDE=\"*res://addons/guide/guide.gd\"\n\n[debug]\n\ngdscript/warnings/untyped_declaration=2\n\n[display]\n\nwindow/size/viewport_width=1920\nwindow/size/viewport_height=1080\nwindow/stretch/aspect=\"expand\"\n\n[dotnet]\n\nproject/assembly_name=\"guide\"\n\n[editor_plugins]\n\nenabled=PackedStringArray(\"res://addons/gdUnit4/plugin.cfg\", \"res://addons/guide/plugin.cfg\")\n\n[gdunit4]\n\nsettings/test/test_lookup_folder=\"tests\"\nui/inspector/tree_view_mode=1\nui/inspector/tree_sort_mode=2\nui/toolbar/run_overall=true\nsettings/common/update_notification_enabled=false\n\n[input]\n\nui_accept={\n\"deadzone\": 0.5,\n\"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\":4194309,\"physical_keycode\":0,\"key_label\":0,\"unicode\":0,\"echo\":false,\"script\":null)\n, 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\":32,\"physical_keycode\":0,\"key_label\":0,\"unicode\":32,\"echo\":false,\"script\":null)\n]\n}\nnarf={\n\"deadzone\": 0.5,\n\"events\": [Object(InputEventMouseButton,\"resource_local_to_scene\":false,\"resource_name\":\"\",\"device\":-1,\"window_id\":0,\"alt_pressed\":false,\"shift_pressed\":false,\"ctrl_pressed\":false,\"meta_pressed\":false,\"button_mask\":0,\"position\":Vector2(0, 0),\"global_position\":Vector2(0, 0),\"factor\":1.0,\"button_index\":4,\"canceled\":false,\"pressed\":false,\"double_click\":false,\"script\":null)\n]\n}\nwqw={\n\"deadzone\": 0.5,\n\"events\": [Object(InputEventMouseButton,\"resource_local_to_scene\":false,\"resource_name\":\"\",\"device\":-1,\"window_id\":0,\"alt_pressed\":false,\"shift_pressed\":false,\"ctrl_pressed\":false,\"meta_pressed\":false,\"button_mask\":8,\"position\":Vector2(297, 40),\"global_position\":Vector2(303, 102),\"factor\":1.0,\"button_index\":4,\"canceled\":false,\"pressed\":true,\"double_click\":false,\"script\":null)\n]\n}\n\n[input_devices]\n\npointing/emulate_mouse_from_touch=false\n\n[physics]\n\n2d/run_on_separate_thread=true\n3d/run_on_separate_thread=true\n\n[rendering]\n\ntextures/vram_compression/import_etc2_astc=true\n"
  },
  {
    "path": "run_tests.cmd",
    "content": "@echo off\nsetlocal\n\nREM Set the Godot binary path\nset GODOT_BIN=c:\\tools\\godot\\4.2\\godot42.exe\n\nREM Check if any arguments were provided\nif \"%~1\"==\"\" (\n    set TEST_ARGS=-a tests\n) else (\n    set TEST_ARGS=%*\n)\n\nREM Run tests without debug mode to avoid hanging on compilation errors\n%GODOT_BIN% -s res://addons/gdUnit4/bin/GdUnitCmdTool.gd %TEST_ARGS%\nSET exit_code=%errorlevel%\n%GODOT_BIN% --headless --quiet -s res://addons/gdUnit4/bin/GdUnitCopyLog.gd %TEST_ARGS%\n\nECHO %exit_code%\nEXIT /B %exit_code%\n"
  },
  {
    "path": "tests/guide_input_matcher.gd",
    "content": "## This is an input matcher for matching G.U.I.D.E. inputs in signals.\nclass_name GUIDEInputMatcher\nextends GdUnitArgumentMatcher\n\nvar _input: GUIDEInput\nfunc _init(input:GUIDEInput) -> void:\n\t_input = input\n\t\nfunc is_match(argument:Variant) -> bool:\n\treturn _input == argument or _input.is_same_as(argument)\n"
  },
  {
    "path": "tests/guide_input_matcher.gd.uid",
    "content": "uid://cmy4sp2x1rftc\n"
  },
  {
    "path": "tests/guide_test_base.gd",
    "content": "class_name GUIDETestBase\nextends GdUnitTestSuite\n\n#------------------- Lifecycle ---------------------------------------------\nvar start_frame:int = 0\nvar runner:GdUnitSceneRunner\n\nfunc after_test() -> void:\n\tprint_f(\"Cleanup phase\")\n\t# Clear all mapping contexts after each test\n\t# since this will modify the dictionary in GUIDE, we make a copy\n\tvar contexts:Array = GUIDE._active_contexts.keys()\n\tfor context:GUIDEMappingContext in contexts:\n\t\tGUIDE.disable_mapping_context(context)\n\t\t\n\tGUIDEInputFormatter.cleanup()\n\n\nfunc before_test() -> void:\n\tprint(\"-----------------------------------\")\n\tstart_frame = Engine.get_process_frames()\n\tprint_f(\"Setup phase\")\n\trunner = scene_runner(auto_free(Node.new()))\n\tGUIDE._input_state._clear()\n\t_setup()\n\tprint_f(\"Test phase\")\n\t\nfunc _setup() -> void:\n\tpass\n\n#------------------- Setup -------------------------------------------------\n\nfunc mapping_context() -> GUIDEMappingContext:\n\treturn GUIDEMappingContext.new()\n\n\n@warning_ignore(\"shadowed_variable_base_class\")\nfunc action(name:String, value_type:GUIDEAction.GUIDEActionValueType) -> GUIDEAction:\n\tvar result := GUIDEAction.new()\n\tresult.name = name\n\tresult.action_value_type = value_type\n\treturn result\n\n\n@warning_ignore(\"shadowed_variable_base_class\")\nfunc action_bool(name:String = \"action\") -> GUIDEAction:\n\treturn action(name, GUIDEAction.GUIDEActionValueType.BOOL)\n\n\n@warning_ignore(\"shadowed_variable_base_class\")\nfunc action_1d(name:String = \"action\") -> GUIDEAction:\n\treturn action(name, GUIDEAction.GUIDEActionValueType.AXIS_1D)\n\n\n@warning_ignore(\"shadowed_variable_base_class\")\nfunc action_2d(name:String = \"action\") -> GUIDEAction:\n\treturn action(name, GUIDEAction.GUIDEActionValueType.AXIS_2D)\n\n\n@warning_ignore(\"shadowed_variable_base_class\")\nfunc action_3d(name:String = \"action\") -> GUIDEAction:\n\treturn action(name, GUIDEAction.GUIDEActionValueType.AXIS_3D)\n\n@warning_ignore(\"shadowed_variable\")\nfunc input_action(action:GUIDEAction) -> GUIDEInputAction:\n\tvar result := GUIDEInputAction.new()\n\tresult.action = action\n\treturn result\n\t\n\t\n\t\nfunc input_key(key:Key) -> GUIDEInputKey:\n\tvar result := GUIDEInputKey.new()\n\tresult.key = key\n\treturn result\n\t\n\t\nfunc input_any() -> GUIDEInputAny:\n\tvar result := GUIDEInputAny.new()\n\treturn result\n\nfunc input_mouse_button(button:MouseButton = MOUSE_BUTTON_LEFT) -> GUIDEInputMouseButton:\t\n\tvar result:GUIDEInputMouseButton = GUIDEInputMouseButton.new()\n\tresult.button = button\n\treturn result\n\nfunc input_mouse_axis_1d(axis:GUIDEInputMouseAxis1D.GUIDEInputMouseAxis) -> GUIDEInputMouseAxis1D:\t\n\tvar result := GUIDEInputMouseAxis1D.new()\n\tresult.axis = axis\n\treturn result\n\nfunc input_mouse_axis_2d() -> GUIDEInputMouseAxis2D:\n\treturn GUIDEInputMouseAxis2D.new()\n\nfunc input_mouse_position() -> GUIDEInputMousePosition:\n\treturn GUIDEInputMousePosition.new()\n\t\nfunc input_joy_button(button:JoyButton) -> GUIDEInputJoyButton:\n\tvar result := GUIDEInputJoyButton.new()\n\tresult.button = button\n\treturn result\n\nfunc input_joy_axis_1d(axis:JoyAxis) -> GUIDEInputJoyAxis1D:\n\tvar result := GUIDEInputJoyAxis1D.new()\n\tresult.axis = axis\n\treturn result\n\t\nfunc input_joy_axis_2d(x:JoyAxis, y:JoyAxis) -> GUIDEInputJoyAxis2D:\n\tvar result := GUIDEInputJoyAxis2D.new()\n\tresult.x = x\n\tresult.y = y\n\treturn result\n\t\nfunc input_touch_axis_1d(axis:GUIDEInputTouchAxis1D.GUIDEInputTouchAxis, finger_index:int = 0) -> GUIDEInputTouchAxis1D:\n\tvar result := GUIDEInputTouchAxis1D.new()\n\tresult.axis = axis\n\tresult.finger_index = finger_index\n\treturn result\n\t\nfunc input_touch_axis_2d(finger_index:int = 0) -> GUIDEInputTouchAxis2D:\n\tvar result := GUIDEInputTouchAxis2D.new()\n\tresult.finger_index = finger_index\n\treturn result\n\nfunc input_touch_position(finger_index:int = 0, finger_count:int = 1)-> GUIDEInputTouchPosition:\n\tvar result := GUIDEInputTouchPosition.new()\n\tresult.finger_count = finger_count\n\tresult.finger_index = finger_index\n\treturn result\n\t\nfunc input_touch_distance()-> GUIDEInputTouchDistance:\n\treturn GUIDEInputTouchDistance.new()\n\nfunc input_touch_angle()-> GUIDEInputTouchAngle:\n\treturn GUIDEInputTouchAngle.new()\n\t\nfunc modifier_virtual_cursor(initial_position:Vector2 = Vector2(0.5, 0.5), \\\n\t\tspeed:Vector3=Vector3.ONE, \\\n\t\tscreen_scale:GUIDEModifierVirtualCursor.ScreenScale = GUIDEModifierVirtualCursor.ScreenScale.LONGER_AXIS, \\\n\t\tapply_delta_time:bool = true\n\t\t) -> GUIDEModifierVirtualCursor:\n\tvar result := GUIDEModifierVirtualCursor.new()\n\tresult.initial_position = initial_position\n\tresult.speed = speed\n\tresult.screen_scale = screen_scale\n\tresult.apply_delta_time = apply_delta_time\n\treturn result\n\t\n\t\nfunc modifier_input_swizzle(operation:GUIDEModifierInputSwizzle.GUIDEInputSwizzleOperation = \\\n\t\tGUIDEModifierInputSwizzle.GUIDEInputSwizzleOperation.YXZ) -> GUIDEModifierInputSwizzle:\n\tvar result := GUIDEModifierInputSwizzle.new()\n\tresult.order = operation\n\treturn result\n\t\n\t\nfunc modifier_negate(x:bool = true, y:bool = true, z:bool = true) -> GUIDEModifierNegate:\n\tvar result := GUIDEModifierNegate.new()\n\tresult.x = x\n\tresult.y = y\n\tresult.z = z\n\treturn result\n\n\t\nfunc modifier_map_range(imin:float, imax:float, omin:float, omax:float, x:bool = true, y:bool = true, z:bool = true) -> \\\n\tGUIDEModifierMapRange:\n\tvar result := GUIDEModifierMapRange.new()\n\tresult.input_min = imin\n\tresult.input_max = imax\n\tresult.output_min = omin\n\tresult.output_max = omax\n\tresult.x = x\n\tresult.y = y\n\tresult.z = z\n\treturn result\n\nfunc trigger_down() -> GUIDETriggerDown:\n\treturn GUIDETriggerDown.new()\n\t\n\t\nfunc trigger_hold(hold_treshold:float = 1.0, is_one_shot:bool = false) -> GUIDETriggerHold:\n\tvar result := GUIDETriggerHold.new()\n\tresult.hold_treshold = hold_treshold\n\tresult.is_one_shot = is_one_shot\n\treturn result\n\t\n\t\nfunc trigger_pressed() -> GUIDETriggerPressed:\n\treturn GUIDETriggerPressed.new()\n\nfunc trigger_released() -> GUIDETriggerReleased:\n\treturn GUIDETriggerReleased.new()\n\nfunc trigger_hair(threshold: float = 0.5) -> GUIDETriggerHair:\n\tvar result := GUIDETriggerHair.new()\n\tresult.actuation_threshold = threshold\n\treturn result\n\n@warning_ignore(\"shadowed_variable\")\nfunc trigger_chorded_action(action:GUIDEAction) -> GUIDETriggerChordedAction:\n\tvar result := GUIDETriggerChordedAction.new()\t\t\n\tresult.action = action\n\treturn result\n\t\t\nfunc trigger_combo(steps:Array[GUIDEAction]) -> GUIDETriggerCombo:\n\tvar result := GUIDETriggerCombo.new()\n\tfor step:GUIDEAction in steps:\n\t\tvar combo_step := GUIDETriggerComboStep.new()\n\t\tcombo_step.action = step\n\t\tresult.steps.append(combo_step)\n\treturn result\n\t\t\n@warning_ignore(\"shadowed_variable\")\nfunc map(context:GUIDEMappingContext, action:GUIDEAction, input:GUIDEInput, \\\n\tmodifiers:Array[GUIDEModifier] = [], triggers:Array[GUIDETrigger] = []) -> void:\n\tvar action_mapping:GUIDEActionMapping = null\n\t\n\tfor mapping in context.mappings:\n\t\tif mapping.action == action:\n\t\t\taction_mapping = mapping\n\t\t\tbreak\n\t\t\t\n\tif action_mapping == null:\n\t\taction_mapping = GUIDEActionMapping.new()\n\t\taction_mapping.action = action\n\t\tcontext.mappings.append(action_mapping)\t\n\t\n\tvar input_mapping := GUIDEInputMapping.new()\n\tinput_mapping.input = input\n\tinput_mapping.modifiers = modifiers\n\tinput_mapping.triggers = triggers\n\t\n\taction_mapping.input_mappings.append(input_mapping)\n\n\n#------------------ Input simulation ----------------------------------------\n\nfunc mouse_down(button:MouseButton, wait:bool =  true) -> void:\n\tvar input := InputEventMouseButton.new()\n\tinput.button_index = button\n\tinput.pressed= true\n\tinput.position = get_viewport().get_mouse_position()\n\tInput.parse_input_event(input)\n\tprint_f(\"Mouse down %s\" % button)\n\tif wait:\n\t\tawait wait_f(2)\n\n\nfunc mouse_up(button:MouseButton, wait:bool =  true) -> void:\n\tvar input := InputEventMouseButton.new()\n\tinput.button_index = button\n\tinput.pressed= false\n\tinput.position = get_viewport().get_mouse_position()\n\tInput.parse_input_event(input)\n\tprint_f(\"Mouse up %s\" % button)\n\tif wait:\n\t\tawait wait_f(2)\n\n\nfunc tap_mouse(button:MouseButton) -> void:\n\tawait mouse_down(button)\n\tawait mouse_up(button)\t\t\n\nfunc tap_mouse_at(button:MouseButton, position:Vector2) -> void:\n\tawait mouse_move_to(position)\n\tawait tap_mouse(button)\n\t\t\nfunc key_down(key:Key, wait:bool = true) -> void:\n\tvar input := InputEventKey.new()\n\tinput.physical_keycode = key\n\tinput.pressed = true\n\tInput.parse_input_event(input)\n\tprint_f(\"Key down %s\" % OS.get_keycode_string(DisplayServer.keyboard_get_label_from_physical(key)))\n\tif wait:\n\t\tawait wait_f(2)\n\t\nfunc key_up(key:Key, wait:bool = true) -> void:\n\tvar input := InputEventKey.new()\n\tinput.physical_keycode = key\n\tinput.pressed = false\n\tInput.parse_input_event(input)\n\tprint_f(\"Key up %s\" % OS.get_keycode_string(DisplayServer.keyboard_get_label_from_physical(key)))\n\tif wait:\n\t\tawait wait_f(2)\n\nfunc keys_down(keys:Array[Key], wait:bool = true) -> void:\n\tfor key in keys:\n\t\tkey_down(key, false)\n\t\t\n\tif wait:\n\t\tawait wait_f(2)\n\nfunc keys_up(keys:Array[Key], wait:bool = true) -> void:\n\tfor key in keys:\n\t\tkey_up(key, false)\n\t\t\n\tif wait:\n\t\tawait wait_f(2)\n\t\n\t\nfunc tap_key(key:Key) -> void:\n\tawait key_down(key)\n\tawait key_up(key)\n\t\n\t\nfunc tap_keys(keys:Array[Key]) -> void:\n\tawait keys_down(keys)\n\tawait keys_up(keys)\n\t\n\t\nfunc mouse_move_by(delta:Vector2, wait:bool = true) -> void:\n\tvar pos := get_viewport().get_mouse_position() + delta\n\t\n\tif wait:\n\t\tawait mouse_move_to(pos, true)\n\telse:\n\t\tmouse_move_to(pos, false)\n\t\t\n\t\t\n\t\t\n## Moves the mouse to the given position in world coordinates.\t\t\nfunc mouse_move_to(position:Vector2, wait:bool = true) -> void:\n\tprint_f(\"Mouse move to %s (from %s)\" % [position, runner.get_global_mouse_position()])\n\t\n\trunner.simulate_mouse_move(position)\n\t\n\tif wait:\n\t\tawait wait_f(2)\n\nfunc joy_button_down(button:JoyButton, wait:bool = true) -> void:\n\tvar input := InputEventJoypadButton.new()\n\tinput.button_index = button\n\tinput.pressed = true\n\tInput.parse_input_event(input)\n\tprint_f(\"Joy button down %s\" % button)\n\tif wait:\n\t\tawait wait_f(2)\n\t\nfunc joy_button_up(button:JoyButton, wait:bool = true) -> void:\n\tvar input := InputEventJoypadButton.new()\n\tinput.button_index = button\n\tinput.pressed = false\n\tInput.parse_input_event(input)\n\tprint_f(\"Joy button up %s\" % button)\n\tif wait:\n\t\tawait wait_f(2)\n\t\t\nfunc tap_joy_button(button:JoyButton) -> void:\n\tawait joy_button_down(button)\n\tawait joy_button_up(button)\n\t\n\t\nfunc joy_axis(axis:JoyAxis, value:float, wait:bool = true) -> void:\n\tvar input := InputEventJoypadMotion.new()\n\tinput.axis = axis\n\tinput.axis_value = value\n\tInput.parse_input_event(input)\n\tprint_f(\"Joy axis %s: %s\" % [axis, value])\n\tif wait:\n\t\tawait wait_f(2)\n\t\t\nfunc finger_down(index:int, position:Vector2, wait:bool = true) -> void:\n\tvar input := InputEventScreenTouch.new()\n\tinput.index = index\n\tinput.pressed = true\n\tinput.position = position\n\tInput.parse_input_event(input)\n\tprint_f(\"Finger down %s (%s)\" % [index, position])\n\tif wait:\n\t\tawait wait_f(2)\n\t\t\nfunc finger_up(index:int, wait:bool = true) -> void:\n\tvar input := InputEventScreenTouch.new()\n\tinput.index = index\n\tinput.pressed = false\n\tInput.parse_input_event(input)\n\tprint_f(\"Finger up %s\" % index)\n\tif wait:\n\t\tawait wait_f(2)\n\t\t\n\t\t\nfunc tap_finger(index:int, position:Vector2) -> void:\n\tawait finger_down(index, position)\n\tawait finger_up(index)\n\t\n\t\nfunc finger_move(index:int, to:Vector2, wait:bool = true) -> void:\n\tvar input := InputEventScreenDrag.new()\n\tinput.index = index\n\tinput.position = to\n\tprint_f(\"Finger move %s (%s)\" % [index, to])\n\tInput.parse_input_event(input)\n\tif wait:\n\t\tawait wait_f(2)\n\t\n\t\t\nfunc world_to_viewport(global_position:Vector2) -> Vector2:\n\tvar final_transform := get_viewport().get_final_transform()\n\treturn final_transform.basis_xform(global_position) + final_transform.origin\n\n#------------------ Custom asserts -------------------------------------------\n\n## Watches the given action for emitted signals. Returns a watcher which can\n## be used to assert on the received signals.\nfunc watch(to_watch: GUIDEAction) -> WatchedAction:\n\treturn WatchedAction.new(to_watch, self)\n\n## Asserts that the given action is currently reporting as triggered.\n@warning_ignore(\"shadowed_variable\")\nfunc assert_is_triggered(action:GUIDEAction) -> void:\n\tassert_bool(action.is_triggered()) \\\n\t\t.append_failure_message(\"Action should be triggered but is not.\") \\\n\t\t.is_true()\n\n\n## Asserts that the given action is currently not reporting as triggered.\n@warning_ignore(\"shadowed_variable\")\nfunc assert_is_not_triggered(action:GUIDEAction) -> void:\n\tassert_bool(action.is_triggered()) \\\n\t\t.append_failure_message(\"Action should not be triggered but is.\") \\\n\t\t.is_false()\n\n\n## Asserts that the given action has the given axis 1D value.\n@warning_ignore(\"shadowed_variable\")\nfunc assert_axis_1d(action:GUIDEAction, expected_value:float, deviation:float = 0.1) -> void:\n\tassert_float(action.value_axis_1d) \\\n\t\t.append_failure_message(\"Action axis 1D value is not the expected value.\") \\\n\t\t.is_equal_approx(expected_value, deviation) \n\t\t\n\t\t\n## Asserts that the given action has the given axis 2D value.\n@warning_ignore(\"shadowed_variable\")\nfunc assert_axis_2d(action:GUIDEAction, expected_value:Vector2, deviation:Vector2 = Vector2(1.0, 1.0)) -> void:\n\tassert_vector(action.value_axis_2d) \\\n\t\t.append_failure_message(\"Action axis 2D value is not the expected value.\") \\\n\t\t.is_equal_approx(expected_value, deviation)\n\n\n#------------------ Other stuff -------------------------------------------\n@warning_ignore(\"shadowed_variable\")\nfunc log_signals(action:GUIDEAction) -> void:\n\taction.triggered.connect(func() -> void: print_f(\"action triggered: '%s' (%s)\" % [action.name, action._value]))\n\taction.just_triggered.connect(func() -> void: print_f(\"action just_triggered: '%s'\" % action.name))\n\taction.cancelled.connect(func() -> void: print_f(\"action cancelled: '%s'\" % action.name))\n\taction.completed.connect(func() -> void: print_f(\"action completed: '%s'\" % action.name))\n\taction.started.connect(func() -> void: print_f(\"action started: '%s'\" % action.name))\n\taction.ongoing.connect(func() -> void: print_f(\"action ongoing: '%s'\" % action.name))\n\nfunc wait_f(frames:int) -> void:\n\tvar start := get_f()\n\twhile start + frames > get_f():\n\t\tvar tree := get_tree()\n\t\tassert_object(tree)\\\n\t\t\t.is_not_null()\\\n\t\t\t.append_failure_message(\"Got no tree. Did you forget to add an await somewhere?\")\n\t\t\n\t\tawait get_tree().process_frame\n\nfunc wait_seconds(seconds:float) -> void:\n\tawait get_tree().create_timer(seconds).timeout\n\nfunc print_f(text:Variant = \"\") -> void:\n\tprint(\"[%s (%s)] %s\" % [get_f(), Engine.get_process_frames(), text])\n\nfunc get_f() -> int:\n\treturn Engine.get_process_frames() - start_frame\n\n\n\n## A class that watches signals of a GUIDEAction and provides assertions for them.\n##\n## This class tracks how many times each signal has been emitted. The assertion methods\n## automatically reset their counter after checking, so you don't need to call [method reset]\n## between assertions.\n##\n## Thanks to gdUnit4's stack trace scanning, methods starting with [code]assert_[/code] are\n## automatically skipped when reporting line numbers, ensuring that test failures report the\n## correct line in your test file, not inside this helper class.\n##\n## Example:\n## [codeblock]\n## var action := watch(_action)\n## await key_down(KEY_Q)\n## await action.assert_triggered()  # Resets triggered counter automatically\n## await wait_f(1)\n## await action.assert_triggered()  # No reset() needed, works immediately!\n## [/codeblock]\nclass WatchedAction:\n\tvar _action: GUIDEAction\n\tvar _test_suite: GdUnitTestSuite\n\tvar _triggered_count: int = 0\n\tvar _just_triggered_count: int = 0\n\tvar _started_count: int = 0\n\tvar _ongoing_count: int = 0\n\tvar _completed_count: int = 0\n\tvar _cancelled_count: int = 0\n\n\tfunc _init(action: GUIDEAction, test_suite: GdUnitTestSuite) -> void:\n\t\t_action = action\n\t\t_test_suite = test_suite\n\t\t_action.triggered.connect(func() -> void: _triggered_count += 1)\n\t\t_action.just_triggered.connect(func() -> void: _just_triggered_count += 1)\n\t\t_action.started.connect(func() -> void: _started_count += 1)\n\t\t_action.ongoing.connect(func() -> void: _ongoing_count += 1)\n\t\t_action.completed.connect(func() -> void: _completed_count += 1)\n\t\t_action.cancelled.connect(func() -> void: _cancelled_count += 1)\n\n\t## Asserts that the 'triggered' signal was emitted.\n\t## Waits up to [param timeout] seconds for the signal.\n\t## Automatically resets the counter after checking.\n\tfunc assert_triggered(timeout: float = 1.0) -> void:\n\t\t_test_suite.assert_bool(await _was_emitted(\"triggered\", timeout)) \\\n\t\t\t.append_failure_message(\"Expected 'triggered' signal to be emitted\") \\\n\t\t\t.is_true()\n\n\t## Asserts that the 'triggered' signal was NOT emitted.\n\tfunc assert_not_triggered() -> void:\n\t\t_test_suite.assert_bool(_was_not_emitted(\"triggered\")) \\\n\t\t\t.append_failure_message(\"Expected 'triggered' signal to NOT be emitted\") \\\n\t\t\t.is_true()\n\n\t## Asserts that the 'just_triggered' signal was emitted.\n\t## Waits up to [param timeout] seconds for the signal.\n\t## Automatically resets the counter after checking.\n\tfunc assert_just_triggered(timeout: float = 1.0) -> void:\n\t\t_test_suite.assert_bool(await _was_emitted(\"just_triggered\", timeout)) \\\n\t\t\t.append_failure_message(\"Expected 'just_triggered' signal to be emitted\") \\\n\t\t\t.is_true()\n\n\t## Asserts that the 'just_triggered' signal was NOT emitted.\n\tfunc assert_not_just_triggered() -> void:\n\t\t_test_suite.assert_bool(_was_not_emitted(\"just_triggered\")) \\\n\t\t\t.append_failure_message(\"Expected 'just_triggered' signal to NOT be emitted\") \\\n\t\t\t.is_true()\n\n\t## Asserts that the 'started' signal was emitted.\n\t## Waits up to [param timeout] seconds for the signal.\n\t## Automatically resets the counter after checking.\n\tfunc assert_started(timeout: float = 1.0) -> void:\n\t\t_test_suite.assert_bool(await _was_emitted(\"started\", timeout)) \\\n\t\t\t.append_failure_message(\"Expected 'started' signal to be emitted\") \\\n\t\t\t.is_true()\n\n\t## Asserts that the 'started' signal was NOT emitted.\n\tfunc assert_not_started() -> void:\n\t\t_test_suite.assert_bool(_was_not_emitted(\"started\")) \\\n\t\t\t.append_failure_message(\"Expected 'started' signal to NOT be emitted\") \\\n\t\t\t.is_true()\n\n\t## Asserts that the 'ongoing' signal was emitted.\n\t## Waits up to [param timeout] seconds for the signal.\n\t## Automatically resets the counter after checking.\n\tfunc assert_ongoing(timeout: float = 1.0) -> void:\n\t\t_test_suite.assert_bool(await _was_emitted(\"ongoing\", timeout)) \\\n\t\t\t.append_failure_message(\"Expected 'ongoing' signal to be emitted\") \\\n\t\t\t.is_true()\n\n\t## Asserts that the 'ongoing' signal was NOT emitted.\n\tfunc assert_not_ongoing() -> void:\n\t\t_test_suite.assert_bool(_was_not_emitted(\"ongoing\")) \\\n\t\t\t.append_failure_message(\"Expected 'ongoing' signal to NOT be emitted\") \\\n\t\t\t.is_true()\n\n\t## Asserts that the 'completed' signal was emitted.\n\t## Waits up to [param timeout] seconds for the signal.\n\t## Automatically resets the counter after checking.\n\tfunc assert_completed(timeout: float = 1.0) -> void:\n\t\t_test_suite.assert_bool(await _was_emitted(\"completed\", timeout)) \\\n\t\t\t.append_failure_message(\"Expected 'completed' signal to be emitted\") \\\n\t\t\t.is_true()\n\n\t## Asserts that the 'completed' signal was NOT emitted.\n\tfunc assert_not_completed() -> void:\n\t\t_test_suite.assert_bool(_was_not_emitted(\"completed\")) \\\n\t\t\t.append_failure_message(\"Expected 'completed' signal to NOT be emitted\") \\\n\t\t\t.is_true()\n\n\t## Asserts that the 'cancelled' signal was emitted.\n\t## Waits up to [param timeout] seconds for the signal.\n\t## Automatically resets the counter after checking.\n\tfunc assert_cancelled(timeout: float = 1.0) -> void:\n\t\t_test_suite.assert_bool(await _was_emitted(\"cancelled\", timeout)) \\\n\t\t\t.append_failure_message(\"Expected 'cancelled' signal to be emitted\") \\\n\t\t\t.is_true()\n\n\t## Asserts that the 'cancelled' signal was NOT emitted.\n\tfunc assert_not_cancelled() -> void:\n\t\t_test_suite.assert_bool(_was_not_emitted(\"cancelled\")) \\\n\t\t\t.append_failure_message(\"Expected 'cancelled' signal to NOT be emitted\") \\\n\t\t\t.is_true()\n\t\t\n\t## Resets internal counters\n\tfunc reset() -> void:\n\t\t_triggered_count = 0\n\t\t_just_triggered_count  = 0\n\t\t_started_count = 0\n\t\t_ongoing_count = 0\n\t\t_completed_count = 0\n\t\t_cancelled_count = 0\n\n\tfunc _get_counter(signal_name: String) -> int:\n\t\tmatch signal_name:\n\t\t\t\"triggered\": return _triggered_count\n\t\t\t\"just_triggered\": return _just_triggered_count\n\t\t\t\"started\": return _started_count\n\t\t\t\"ongoing\": return _ongoing_count\n\t\t\t\"completed\": return _completed_count\n\t\t\t\"cancelled\": return _cancelled_count\n\t\treturn 0\n\n\tfunc _reset_counter(signal_name: String) -> void:\n\t\tmatch signal_name:\n\t\t\t\"triggered\": _triggered_count = 0\n\t\t\t\"just_triggered\": _just_triggered_count = 0\n\t\t\t\"started\": _started_count = 0\n\t\t\t\"ongoing\": _ongoing_count = 0\n\t\t\t\"completed\": _completed_count = 0\n\t\t\t\"cancelled\": _cancelled_count = 0\n\n\tfunc _was_emitted(signal_name: String, timeout: float) -> bool:\n\t\tvar start_time := Time.get_ticks_msec() / 1000.0\n\t\tvar success := true\n\t\twhile _get_counter(signal_name) == 0:\n\t\t\tif (Time.get_ticks_msec() / 1000.0) - start_time > timeout:\n\t\t\t\tsuccess = false\n\t\t\t\tbreak\n\t\t\tawait (Engine.get_main_loop() as SceneTree).process_frame\n\t\t\n\t\t_reset_counter(signal_name)\n\t\treturn success\n\n\tfunc _was_not_emitted(signal_name: String) -> bool:\n\t\treturn _get_counter(signal_name) == 0\n"
  },
  {
    "path": "tests/guide_test_base.gd.uid",
    "content": "uid://iic1ftmb2ci\n"
  },
  {
    "path": "tests/test_action_prioritization.gd",
    "content": "extends GUIDETestBase\n\nvar _context: GUIDEMappingContext\nvar _action_a: GUIDEAction\nvar _action_lt_a: GUIDEAction\nvar _modifier_lt: GUIDEAction\n\nfunc _setup() -> void:\n\t_context = mapping_context()\n\t_action_a = action_bool(\"a\")\n\t_action_lt_a = action_bool(\"lt+a\")\n\t_modifier_lt = action_bool(\"lt\")\n\n\t# Modifier action: hold left trigger\n\tmap(_context, _modifier_lt, input_joy_axis_1d(JOY_AXIS_TRIGGER_LEFT))\n\n\tvar a_button := input_joy_button(JOY_BUTTON_A)\n\n\t# Chorded action: LT + A has higher priority because defined earlier\n\tmap(_context, _action_lt_a, a_button, [], [trigger_chorded_action(_modifier_lt), trigger_pressed()])\n\n\t# Base action: press A\n\tmap(_context, _action_a, a_button, [] , [trigger_pressed()])\n\n\tGUIDE.enable_mapping_context(_context)\n\n\nfunc test_a_triggers_without_modifier() -> void:\n\tvar watched_a := watch(_action_a)\n\tvar watched_lt_a := watch(_action_lt_a)\n\n\t# WHEN: I press A without holding LT\n\tawait tap_joy_button(JOY_BUTTON_A)\n\n\t# THEN: only the simple A action triggers\n\tawait watched_a.assert_triggered()\n\twatched_lt_a.assert_not_triggered()\n\n\nfunc test_chorded_action_blocks_lower_priority() -> void:\n\tvar watched_a := watch(_action_a)\n\tvar watched_lt_a := watch(_action_lt_a)\n\n\tfor a:GUIDEAction in GUIDE._actions_sharing_input.keys():\n\t\tprint(\"%s > %s\" % [a.name, GUIDE._actions_sharing_input[a][0].name])\n\tawait joy_axis(JOY_AXIS_TRIGGER_LEFT, 1.0)\n\tawait tap_joy_button(JOY_BUTTON_A)\n\tawait joy_axis(JOY_AXIS_TRIGGER_LEFT, 0.0)\n\n\t# THEN: the chorded action (LT + A) triggers and blocks the plain A action\n\tawait watched_lt_a.assert_triggered()\n\twatched_a.assert_not_triggered()\n\n"
  },
  {
    "path": "tests/test_action_prioritization.gd.uid",
    "content": "uid://yq05vjxu3vpq\n"
  },
  {
    "path": "tests/test_action_signals.gd",
    "content": "## This test verifies the signals of GUIDEAction.\nextends GUIDETestBase\n\nvar _action: GUIDEAction\nvar _context: GUIDEMappingContext\n\nfunc _setup() -> void:\n\t_action = action_bool(\"test_action\")\n\t_context = mapping_context()\n\n\n## Helper to map a key to the action with optional triggers.\nfunc _map_key(key: Key, triggers: Array[GUIDETrigger] = []) -> void:\n\tmap(_context, _action, input_key(key), [], triggers)\n\n\n## Tests the basic triggered and just_triggered signals.\nfunc test_triggered_signals() -> void:\n\t_map_key(KEY_A)\n\tGUIDE.enable_mapping_context(_context)\n\n\tvar watched := watch(_action)\n\n\t# Initial state: nothing triggered\n\twatched.assert_not_triggered()\n\twatched.assert_not_just_triggered()\n\n\t# Press key\n\tawait key_down(KEY_A)\n\n\t# THEN: triggered and just_triggered are emitted\n\tawait watched.assert_triggered()\n\tawait watched.assert_just_triggered()\n\n\t# Keep key down for another frame\n\tawait wait_f(1)\n\n\t# THEN: triggered is emitted again, but just_triggered is not\n\tawait watched.assert_triggered()\n\twatched.assert_not_just_triggered()\n\n\t# Release key\n\tawait key_up(KEY_A)\n\twatched.reset()\n\n\t# wait\n\tawait wait_f(10)\n\n\t# no more triggered events are sent\n\twatched.assert_not_triggered()\n\n\n## Tests the started, ongoing, and completed signals using a Hold trigger.\nfunc test_hold_signals() -> void:\n\tvar hold := trigger_hold(0.5)\n\t_map_key(KEY_H, [hold])\n\tGUIDE.enable_mapping_context(_context)\n\n\tvar watched := watch(_action)\n\n\t# Press key\n\tawait key_down(KEY_H)\n\n\t# THEN: started and ongoing are emitted, but not triggered yet\n\tawait watched.assert_started()\n\tawait watched.assert_ongoing()\n\twatched.assert_not_triggered()\n\n\t# Wait some time but not enough for the hold\n\tawait wait_seconds(0.1)\n\n\t# THEN: ongoing is emitted, but started and triggered are not\n\tawait watched.assert_ongoing()\n\twatched.assert_not_started()\n\twatched.assert_not_triggered()\n\n\t# Wait enough for the hold to trigger\n\tawait wait_seconds(0.5)\n\n\t# THEN: triggered is emitted\n\tawait watched.assert_triggered()\n\n\t# Release key\n\tawait key_up(KEY_H)\n\n\t# THEN: completed is emitted\n\tawait watched.assert_completed()\n\n\n## Tests the cancelled signal when a hold is interrupted.\nfunc test_cancelled_signal() -> void:\n\tvar hold := trigger_hold(1.0)\n\t_map_key(KEY_C, [hold])\n\tGUIDE.enable_mapping_context(_context)\n\n\tvar watched := watch(_action)\n\n\t# Press key\n\tawait key_down(KEY_C)\n\n\t# THEN: started and ongoing are emitted\n\tawait watched.assert_started()\n\tawait watched.assert_ongoing()\n\n\t# Release key before hold threshold\n\tawait key_up(KEY_C)\n\n\t# THEN: cancelled and completed are emitted\n\tawait watched.assert_cancelled()\n\tawait watched.assert_completed()\n\twatched.assert_not_triggered()\n\n\n## Tests the completed signal when a regular action is released.\nfunc test_completed_signal() -> void:\n\t_map_key(KEY_B)\n\tGUIDE.enable_mapping_context(_context)\n\n\tvar watched := watch(_action)\n\n\t# Press key\n\tawait key_down(KEY_B)\n\tawait watched.assert_triggered()\n\n\t# Release key\n\tawait key_up(KEY_B)\n\n\t# THEN: completed is emitted\n\tawait watched.assert_completed()\n\n"
  },
  {
    "path": "tests/test_action_signals.gd.uid",
    "content": "uid://bmh5idjsnqs6u\n"
  },
  {
    "path": "tests/test_any_input.gd",
    "content": "extends GUIDETestBase\n\nvar _context:GUIDEMappingContext\nvar _action:GUIDEAction\n\nfunc _setup() -> void:\n\t_context = mapping_context()\n\t_action = action_bool()\n\nfunc test_any_input_works_for_mouse_clicks() -> void:\n\n\tvar input := input_any()\n\tinput.mouse = true\n\tmap(_context, _action, input)\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\n\t# WHEN: i press the mouse button\n\tawait tap_mouse(MOUSE_BUTTON_LEFT)\n\n\t# THEN: the action is triggered\n\tawait watched.assert_triggered()\n\t\n\t\nfunc test_any_input_works_for_mouse_movement() -> void:\n\tvar input := input_any()\n\tinput.mouse_movement = true\n\tinput.minimum_mouse_movement_distance = 2\n\tmap(_context, _action, input)\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\t# WHEN: i move the mouse\n\tawait mouse_move_by(Vector2(3,3))\n\n\t# THEN: the action is triggered\n\tawait watched.assert_triggered()\n\t\n\nfunc test_any_input_adheres_to_mouse_minimum_distance() -> void:\n\tvar input := input_any()\n\tinput.mouse_movement = true\n\tinput.minimum_mouse_movement_distance = 30\n\tmap(_context, _action, input)\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\t# WHEN: i move the mouse just a small bit\n\tawait mouse_move_by(Vector2(3,3))\n\n\t# THEN: the action is not triggered\n\twatched.assert_not_triggered()\n\t\n\n\nfunc test_any_input_works_with_joy_buttons() -> void:\n\tvar input := input_any()\n\tinput.joy_buttons = true\n\tmap(_context, _action, input)\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\t# WHEN: i press the joy button\n\tawait tap_joy_button(JOY_BUTTON_A)\n\n\t# THEN: the action is triggered\n\tawait watched.assert_triggered()\n\t\n\nfunc test_any_input_works_with_joy_axis() -> void:\n\tvar input := input_any()\n\tinput.joy_axes = true\n\tmap(_context, _action, input)\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\t# WHEN: i move the joy axis\n\tawait joy_axis(JOY_AXIS_LEFT_X, 0.5)\n\n\t# THEN: the action is triggered\n\tawait watched.assert_triggered()\n\t\n\tawait joy_axis(JOY_AXIS_LEFT_X, 0)\n\t\nfunc test_any_input_works_with_joy_axis_with_deadzone() -> void:\n\tvar input := input_any()\n\tinput.joy_axes = true\n\tinput.minimum_joy_axis_actuation_strength = 0.7\n\tmap(_context, _action, input)\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\t# WHEN: i move the joy axis just a bit\n\tawait joy_axis(JOY_AXIS_LEFT_X, 0.5)\n\n\t# THEN: the action is not triggered\n\twatched.assert_not_triggered()\n\n\t\nfunc test_any_input_works_with_keyboard() -> void:\n\tvar input := input_any()\n\tinput.keyboard = true\n\tmap(_context, _action, input)\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\t# WHEN: i press the key\n\tawait tap_key(KEY_Q)\n\n\t# THEN: the action is triggered\n\tawait watched.assert_triggered()\n\t\nfunc test_any_input_works_with_touch() -> void:\n\tvar input := input_any()\n\tinput.touch = true\n\tmap(_context, _action, input)\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\t# WHEN: i press the touch\n\tawait tap_finger(0, Vector2(100, 100))\n\n\t# THEN: the action is triggered\n\tawait watched.assert_triggered()\n\t\nfunc test_any_input_handles_queuing_input_correctly() -> void:\n\tvar input := input_any()\n\tinput.joy_buttons = true\n\n\tmap(_context, _action, input)\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\t# when I actuate a button (and not wait)\n\tjoy_button_down(JOY_BUTTON_A, false)\n\n\t# and and axis aftwards\n\tawait joy_axis(JOY_AXIS_LEFT_X, 0.1, true)\n\n\t# then the action is still triggered\n\tawait watched.assert_triggered()\n\n\nfunc test_any_input_with_down_trigger_fires_every_frame() -> void:\n\tvar input := input_any()\n\tinput.keyboard = true\n\n\tmap(_context, _action, input, [], [trigger_down()])\n\tGUIDE.enable_mapping_context(_context)\n\n\tvar watched := watch(_action)\n\n\t# WHEN: I press and hold a key\n\tkey_down(KEY_Q, false)\n\n\t# THEN: the action triggers on the first frame\n\tawait watched.assert_triggered()\n\n\t# AND: it continues to trigger on subsequent frames while held\n\tawait wait_f(1)\n\tawait watched.assert_triggered()\n\n\tawait wait_f(1)\n\tawait watched.assert_triggered()\n\n\t# WHEN: I release the key\n\tawait key_up(KEY_Q)\n\n\t# THEN: completed is emitted\n\tawait watched.assert_completed()\n\n\t# AND: triggered no longer fires on subsequent frames\n\twatched.reset()\n\tawait wait_f(1)\n\twatched.assert_not_triggered()\n\n\nfunc test_any_input_keyboard_stops_when_application_loses_focus() -> void:\n\t# Regression test for https://github.com/godotneers/G.U.I.D.E/issues/189\n\t# When the application loses focus (e.g. Alt+Tab, Win key), Godot clears its\n\t# own input state directly without dispatching InputEvents. G.U.I.D.E must\n\t# mirror this so its shadow state doesn't keep the key \"stuck\" as pressed.\n\tvar input := input_any()\n\tinput.keyboard = true\n\n\tmap(_context, _action, input, [], [trigger_down()])\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\t# WHEN: I press and hold a key\n\tkey_down(KEY_Q, false)\n\n\t# THEN: the action triggers each frame while held\n\tawait watched.assert_triggered()\n\n\t# WHEN: the application loses focus (simulating Alt+Tab / Win key stealing focus)\n\tGUIDE.notification(NOTIFICATION_APPLICATION_FOCUS_OUT)\n\twatched.reset()\n\n\t# THEN: the action no longer fires on subsequent frames\n\tawait wait_f(2)\n\twatched.assert_not_triggered()\n\n\tawait wait_f(2)\n\twatched.assert_not_triggered()\n"
  },
  {
    "path": "tests/test_any_input.gd.uid",
    "content": "uid://rua2arirhyq3\n"
  },
  {
    "path": "tests/test_context_mapping_combine.gd",
    "content": "extends GUIDETestBase\n\nvar _context1: GUIDEMappingContext\nvar _context2: GUIDEMappingContext\nvar _context3: GUIDEMappingContext\nvar _action: GUIDEAction\nvar _action2d: GUIDEAction\n\n\nfunc _setup() -> void:\n\t_context1 = mapping_context()\n\t_context2 = mapping_context()\n\t_context3 = mapping_context()\n\t_action = action_bool(\"some_action_bool\")\n\t_action2d = action_2d(\"some_action_2d\")\n\n## Tests that different mapping contexts with the same action but different\n## inputs both trigger when pressed.\nfunc test_inputs_are_combined() -> void:\n\tvar input1 := input_key(KEY_A)\n\tvar input2 := input_joy_button(JOY_BUTTON_A)\n\tvar trigger := trigger_pressed()\n\tmap(_context1, _action, input1, [], [trigger] )\n\tmap(_context2, _action, input2, [], [trigger] )\n\n\tGUIDE.enable_mapping_context(_context1)\n\tGUIDE.enable_mapping_context(_context2)\n\n\tvar action_watcher := watch(_action)\n\n\tawait tap_key(KEY_A)\n\tawait action_watcher.assert_triggered()\n\tawait wait_f(10)\n\taction_watcher.assert_not_triggered()\n\n\tawait tap_joy_button(JOY_BUTTON_A)\n\tawait action_watcher.assert_triggered()\n\tawait wait_f(10)\n\taction_watcher.assert_not_triggered()\n\n## Tests that actions are overridden when a mapping context is enabled that has\n## the same action and the same input as another enabled mapping context\nfunc test_merging_same_input() -> void:\n\tvar input1 := input_key(KEY_A)\n\tvar swizzle := modifier_input_swizzle()\n\tmap(_context1, _action2d, input1, [])\n\tmap(_context2, _action2d, input1, [swizzle])\n\n\tGUIDE.enable_mapping_context(_context2)\n\tGUIDE.enable_mapping_context(_context1)\n\n\tvar action_watcher := watch(_action2d)\n\n\tawait key_down(KEY_A)\n\tawait action_watcher.assert_triggered()\n\tassert_axis_2d(_action2d, Vector2(1, 0), Vector2(0.001, 0))\n\tawait key_up(KEY_A)\n\n\taction_watcher.reset()\n\tGUIDE.disable_mapping_context(_context1)\n\n\tawait key_down(KEY_A)\n\tawait action_watcher.assert_triggered()\n\tassert_axis_2d(_action2d, Vector2(0, 1), Vector2(0, 0.001))\n\tawait key_up(KEY_A)\n\n\n## https://github.com/godotneers/G.U.I.D.E/issues/67\n## Tests that when multiple contexts with different inputs for the same action\n## are merged, disabling one context while an input from another is held\n## does not cause the action to re-trigger.\nfunc test_merged_contexts_preserve_trigger_state_on_context_disable() -> void:\n\tvar input1 := input_key(KEY_W)\n\tvar input2 := input_joy_axis_2d(JOY_AXIS_LEFT_X, JOY_AXIS_LEFT_Y)\n\tvar trigger := trigger_pressed()\n\tmap(_context1, _action2d, input1, [], [trigger])\n\tmap(_context2, _action2d, input2, [], [trigger])\n\n\t# Enable both contexts (inputs will be merged)\n\tGUIDE.enable_mapping_context(_context1)\n\tGUIDE.enable_mapping_context(_context2)\n\n\tvar action_watcher := watch(_action2d)\n\n\t# Press and hold W\n\tawait key_down(KEY_W)\n\n\t# Action should trigger\n\tawait action_watcher.assert_triggered()\n\n\t# Wait a bit\n\tawait wait_f(10)\n\n\t# Should not trigger again (it's a Pressed trigger)\n\taction_watcher.assert_not_triggered()\n\n\t# Now disable the controller context while W is still held\n\tGUIDE.disable_mapping_context(_context2)\n\n\t# The action should NOT re-trigger even though a new mapping was created\n\t# (because the new trigger's _last_value should be initialized to current W value)\n\taction_watcher.assert_not_triggered()\n\n\t# Release W\n\tawait key_up(KEY_W)\n\n\n## Tests that when a remapping config remaps an input to match another context's input,\n## the merged mapping doesn't create duplicate input mappings for the same input.\n## This verifies that the duplicate check happens after remapping is applied.\nfunc test_remapping_to_same_input_doesnt_create_duplicates() -> void:\n\t# Realistic scenario:\n\t# Context 1 (keyboard): Space key -> jump\n\t# Context 2 (controller): Joy button A -> jump\n\t# User remaps keyboard Space to Joy button A (same as controller's original binding)\n\n\tvar space_key := input_key(KEY_SPACE)\n\tvar joy_button := input_joy_button(JOY_BUTTON_A)\n\tvar trigger := trigger_pressed()\n\n\tmap(_context1, _action, space_key, [], [trigger])\n\tmap(_context2, _action, joy_button, [], [trigger])\n\n\t# Create a remapping that changes context1's Space key to Joy button A\n\t# (same as context2's original input)\n\tvar remapping := GUIDERemappingConfig.new()\n\tvar remapped_button := input_joy_button(JOY_BUTTON_A)\n\tremapping._bind(_context1, _action, remapped_button, 0)\n\n\t# Apply the remapping and enable both contexts\n\tGUIDE.set_remapping_config(remapping)\n\tGUIDE.enable_mapping_context(_context1)\n\tGUIDE.enable_mapping_context(_context2)\n\n\t# Find the action mapping for our action\n\tvar action_mapping:GUIDEActionMapping = null\n\tfor mapping in GUIDE._active_action_mappings:\n\t\tif mapping.action == _action:\n\t\t\taction_mapping = mapping\n\t\t\tbreak\n\n\tassert_that(action_mapping).is_not_null()\n\n\t# There should only be ONE input mapping (Joy button A), not two\n\tassert_int(action_mapping.input_mappings.size()).is_equal(1)\n\n\t# Verify the input only triggers once (not twice) when we press the button\n\tvar action_watcher := watch(_action)\n\n\tawait tap_joy_button(JOY_BUTTON_A)\n\n\tawait action_watcher.assert_triggered()\n\n\t# Wait a bit - action should complete normally\n\tawait wait_f(10)\n\taction_watcher.assert_not_triggered()\n\n\n## Edge case: Multiple contexts all remap to the same input.\n## Only one input mapping should exist, not multiple.\nfunc test_multiple_contexts_remap_to_same_input() -> void:\n\t# Three contexts with different inputs, all remap to Joy A\n\tvar space_key := input_key(KEY_SPACE)\n\tvar enter_key := input_key(KEY_ENTER)\n\tvar w_key := input_key(KEY_W)\n\tvar trigger := trigger_pressed()\n\n\tmap(_context1, _action, space_key, [], [trigger])\n\tmap(_context2, _action, enter_key, [], [trigger])\n\tmap(_context3, _action, w_key, [], [trigger])\n\n\t# All three remap to Joy button A\n\tvar remapping := GUIDERemappingConfig.new()\n\tvar joy_a := input_joy_button(JOY_BUTTON_A)\n\tremapping._bind(_context1, _action, joy_a, 0)\n\tremapping._bind(_context2, _action, joy_a, 0)\n\tremapping._bind(_context3, _action, joy_a, 0)\n\n\tGUIDE.set_remapping_config(remapping)\n\tGUIDE.enable_mapping_context(_context1)\n\tGUIDE.enable_mapping_context(_context2)\n\tGUIDE.enable_mapping_context(_context3)\n\n\t# Find the action mapping\n\tvar action_mapping:GUIDEActionMapping = null\n\tfor mapping in GUIDE._active_action_mappings:\n\t\tif mapping.action == _action:\n\t\t\taction_mapping = mapping\n\t\t\tbreak\n\n\tassert_that(action_mapping).is_not_null()\n\t# Should only have ONE input mapping for Joy A, not three\n\tassert_int(action_mapping.input_mappings.size()).is_equal(1)\n\n\t# Verify it works correctly\n\tvar action_watcher := watch(_action)\n\tawait tap_joy_button(JOY_BUTTON_A)\n\tawait action_watcher.assert_triggered()\n\n\n## Edge case: Remapping to null (unbinding).\n## The unbound input should be removed, other context's input should remain.\nfunc test_remapping_to_null_unbinding() -> void:\n\tvar space_key := input_key(KEY_SPACE)\n\tvar joy_a := input_joy_button(JOY_BUTTON_A)\n\tvar trigger := trigger_pressed()\n\n\tmap(_context1, _action, space_key, [], [trigger])\n\tmap(_context2, _action, joy_a, [], [trigger])\n\n\t# Unbind context1's Space key\n\tvar remapping := GUIDERemappingConfig.new()\n\tremapping._unbind(_context1, _action, 0)\n\n\tGUIDE.set_remapping_config(remapping)\n\tGUIDE.enable_mapping_context(_context1)\n\tGUIDE.enable_mapping_context(_context2)\n\n\t# Find the action mapping\n\tvar action_mapping:GUIDEActionMapping = null\n\tfor mapping in GUIDE._active_action_mappings:\n\t\tif mapping.action == _action:\n\t\t\taction_mapping = mapping\n\t\t\tbreak\n\n\tassert_that(action_mapping).is_not_null()\n\t# Should only have Joy A (Space was unbound)\n\tassert_int(action_mapping.input_mappings.size()).is_equal(1)\n\tassert_that(action_mapping.input_mappings[0].input).is_not_null()\n\n\tvar action_watcher := watch(_action)\n\n\t# Space should not trigger\n\tawait tap_key(KEY_SPACE)\n\taction_watcher.assert_not_triggered()\n\n\t# Joy A should trigger\n\tawait tap_joy_button(JOY_BUTTON_A)\n\tawait action_watcher.assert_triggered()\n\n\n## Edge case: Priority interaction with remapping.\n## When contexts have same priority, more recently enabled context should win.\nfunc test_priority_interaction_with_remapping() -> void:\n\tvar space_key := input_key(KEY_SPACE)\n\tvar joy_a := input_joy_button(JOY_BUTTON_A)\n\tvar trigger := trigger_pressed()\n\n\t# Both contexts have same priority (default 0)\n\tmap(_context1, _action, space_key, [], [trigger])\n\tmap(_context2, _action, joy_a, [], [trigger])\n\n\t# Context1 remaps to Joy A (same as context2's original)\n\tvar remapping := GUIDERemappingConfig.new()\n\tremapping._bind(_context1, _action, input_joy_button(JOY_BUTTON_A), 0)\n\n\tGUIDE.set_remapping_config(remapping)\n\tGUIDE.enable_mapping_context(_context1)\n\t# Enable context2 AFTER context1 (more recent)\n\tGUIDE.enable_mapping_context(_context2)\n\n\t# Find the action mapping\n\tvar action_mapping:GUIDEActionMapping = null\n\tfor mapping in GUIDE._active_action_mappings:\n\t\tif mapping.action == _action:\n\t\t\taction_mapping = mapping\n\t\t\tbreak\n\n\tassert_that(action_mapping).is_not_null()\n\t# Should have only one Joy A mapping (context2 wins as it was enabled more recently)\n\tassert_int(action_mapping.input_mappings.size()).is_equal(1)\n\n\t# Verify it triggers correctly\n\tvar action_watcher := watch(_action)\n\tawait tap_joy_button(JOY_BUTTON_A)\n\tawait action_watcher.assert_triggered()\n\n\n## Edge case: Remapping config applied after contexts are already enabled.\n## The remapping should take effect immediately.\nfunc test_remapping_applied_after_contexts_enabled() -> void:\n\tvar space_key := input_key(KEY_SPACE)\n\tvar joy_a := input_joy_button(JOY_BUTTON_A)\n\tvar trigger := trigger_pressed()\n\n\tmap(_context1, _action, space_key, [], [trigger])\n\tmap(_context2, _action, joy_a, [], [trigger])\n\n\t# Enable contexts FIRST\n\tGUIDE.enable_mapping_context(_context1)\n\tGUIDE.enable_mapping_context(_context2)\n\n\tvar action_watcher := watch(_action)\n\n\t# Verify both inputs work initially\n\tawait tap_key(KEY_SPACE)\n\tawait action_watcher.assert_triggered()\n\tawait wait_f(10)\n\n\tawait tap_joy_button(JOY_BUTTON_A)\n\tawait action_watcher.assert_triggered()\n\tawait wait_f(10)\n\n\t# NOW apply remapping - remap context1 to Joy A (creating duplicate)\n\tvar remapping := GUIDERemappingConfig.new()\n\tremapping._bind(_context1, _action, input_joy_button(JOY_BUTTON_A), 0)\n\tGUIDE.set_remapping_config(remapping)\n\n\t# Find the action mapping\n\tvar action_mapping:GUIDEActionMapping = null\n\tfor mapping in GUIDE._active_action_mappings:\n\t\tif mapping.action == _action:\n\t\t\taction_mapping = mapping\n\t\t\tbreak\n\n\tassert_that(action_mapping).is_not_null()\n\t# Should have only one Joy A mapping (no duplicates)\n\tassert_int(action_mapping.input_mappings.size()).is_equal(1)\n\n\t# Space should no longer work\n\tawait tap_key(KEY_SPACE)\n\taction_watcher.assert_not_triggered()\n\n\t# Joy A should work\n\tawait tap_joy_button(JOY_BUTTON_A)\n\tawait action_watcher.assert_triggered()\n\n\n## Edge case: Same context enabled multiple times.\n## Should not create duplicate mappings, just update the timestamp.\nfunc test_same_context_enabled_multiple_times() -> void:\n\tvar space_key := input_key(KEY_SPACE)\n\tvar trigger := trigger_pressed()\n\n\tmap(_context1, _action, space_key, [], [trigger])\n\n\t# Enable the same context three times\n\tGUIDE.enable_mapping_context(_context1)\n\tGUIDE.enable_mapping_context(_context1)\n\tGUIDE.enable_mapping_context(_context1)\n\n\t# Find the action mapping\n\tvar action_mapping:GUIDEActionMapping = null\n\tfor mapping in GUIDE._active_action_mappings:\n\t\tif mapping.action == _action:\n\t\t\taction_mapping = mapping\n\t\t\tbreak\n\n\tassert_that(action_mapping).is_not_null()\n\t# Should have only one input mapping (not three)\n\tassert_int(action_mapping.input_mappings.size()).is_equal(1)\n\n\t# Verify it works correctly\n\tvar action_watcher := watch(_action)\n\tawait tap_key(KEY_SPACE)\n\tawait action_watcher.assert_triggered()\n\n\n## Edge case: Action priority blocking across merged contexts.\n## An action with \"block lower priority\" should block actions from lower priority contexts.\n## Note: Priority is determined by context priority (lower value = higher priority).\n## For same-priority contexts, more recently enabled has higher priority.\nfunc test_action_priority_blocking_across_merged_contexts() -> void:\n\tvar dpad_up := input_joy_button(JOY_BUTTON_DPAD_UP)\n\tvar trigger := trigger_pressed()\n\n\t# Context 1: high priority action that blocks lower priority\n\tvar high_priority_action := action_bool(\"high_priority\")\n\thigh_priority_action.block_lower_priority_actions = true\n\tmap(_context1, high_priority_action, dpad_up, [], [trigger])\n\n\t# Context 2: low priority action (same input)\n\tvar low_priority_action := action_bool(\"low_priority\")\n\tmap(_context2, low_priority_action, dpad_up, [], [trigger])\n\n\t# Enable context1 with higher priority (lower number = higher priority)\n\t# so that high_priority_action can block low_priority_action\n\tGUIDE.enable_mapping_context(_context1, false, 0)\n\tGUIDE.enable_mapping_context(_context2, false, 10)\n\n\tvar high_watcher := watch(high_priority_action)\n\tvar low_watcher := watch(low_priority_action)\n\n\t# Press D-pad up - high priority action should trigger and block the low priority one\n\tawait tap_joy_button(JOY_BUTTON_DPAD_UP)\n\n\tawait high_watcher.assert_triggered()\n\t# Low priority action should be blocked\n\tlow_watcher.assert_not_triggered()\n\n\n## Edge case: Combo mappings with null inputs should not be removed when contexts are merged.\n## This ensures that the unbinding fix doesn't accidentally break combo mappings.\nfunc test_combo_mappings_with_null_inputs_are_preserved() -> void:\n\t# Create two basic actions that the combo will watch for\n\tvar basic_action1 := action_bool(\"basic_action_1\")\n\tvar basic_action2 := action_bool(\"basic_action_2\")\n\n\t# Map those basic actions to keys in context1\n\tmap(_context1, basic_action1, input_key(KEY_A), [], [trigger_pressed()])\n\tmap(_context1, basic_action2, input_key(KEY_B), [], [trigger_pressed()])\n\n\t# Create a combo trigger that watches for the sequence: basic_action1 -> basic_action2\n\tvar combo_trigger := GUIDETriggerCombo.new()\n\tvar step1 := GUIDETriggerComboStep.new()\n\tstep1.action = basic_action1\n\tstep1.time_to_actuate = 0.5\n\tvar step2 := GUIDETriggerComboStep.new()\n\tstep2.action = basic_action2\n\tstep2.time_to_actuate = 0.5\n\tcombo_trigger.steps = [step1, step2]\n\n\t# Create a combo action mapping with null input\n\tvar combo_input_mapping := GUIDEInputMapping.new()\n\tcombo_input_mapping.input = null  # Combo mappings have null input\n\tcombo_input_mapping.triggers = [combo_trigger]\n\n\tvar combo_action_mapping := GUIDEActionMapping.new()\n\tcombo_action_mapping.action = _action\n\tcombo_action_mapping.input_mappings = [combo_input_mapping]\n\n\t# Add the combo mapping to context1\n\t_context1.mappings.append(combo_action_mapping)\n\n\t# Also add a regular mapping in context2 to test merging\n\tmap(_context2, _action, input_key(KEY_SPACE), [], [trigger_pressed()])\n\n\t# Enable both contexts\n\tGUIDE.enable_mapping_context(_context1)\n\tGUIDE.enable_mapping_context(_context2)\n\n\t# Find the action mapping\n\tvar active_mapping:GUIDEActionMapping = null\n\tfor mapping in GUIDE._active_action_mappings:\n\t\tif mapping.action == _action:\n\t\t\tactive_mapping = mapping\n\t\t\tbreak\n\n\tassert_that(active_mapping).is_not_null()\n\t# Should have 2 input mappings: one null (combo) and one Space key\n\tassert_int(active_mapping.input_mappings.size()).is_equal(2)\n\n\t# Verify one of them is null (the combo)\n\tvar has_null_input := false\n\tvar has_space_input := false\n\tfor input_map in active_mapping.input_mappings:\n\t\tif input_map.input == null:\n\t\t\thas_null_input = true\n\t\telif input_map.input is GUIDEInputKey:\n\t\t\thas_space_input = true\n\n\tassert_bool(has_null_input).is_true()\n\tassert_bool(has_space_input).is_true()\n\n## Tests that the hold threshold hint is properly set when merging contexts.\n## This is needed for UI elements like progress bars that show how long until\n## a hold action triggers (e.g., the tap_and_hold example).\nfunc test_hold_threshold_hint_with_merged_contexts() -> void:\n\tvar input1 := input_key(KEY_SPACE)\n\tvar input2 := input_joy_button(JOY_BUTTON_A)\n\tvar hold_trigger := trigger_hold(1.0)  # 1 second hold\n\n\t# Context 1: Space key with hold trigger\n\tmap(_context1, _action, input1, [], [hold_trigger])\n\t# Context 2: Joy button with hold trigger\n\tmap(_context2, _action, input2, [], [trigger_hold(1.0)])\n\n\t# Enable both contexts to trigger merging\n\tGUIDE.enable_mapping_context(_context1)\n\tGUIDE.enable_mapping_context(_context2)\n\n\t# The action should have its hold threshold set\n\tassert_float(_action._trigger_hold_threshold).is_equal(1.0)\n\n\t# Press the key and verify elapsed_ratio increases\n\tvar action_watcher := watch(_action)\n\tawait key_down(KEY_SPACE)\n\n\t# Wait a bit and check that elapsed_ratio is working\n\tawait wait_f(5)  # 5 frames\n\n\t# The action should be ongoing\n\tawait action_watcher.assert_started()\n\n\t# elapsed_ratio should be between 0 and 1 (not stuck at 0)\n\tassert_bool(_action.elapsed_ratio > 0.0).is_true()\n\tassert_bool(_action.elapsed_ratio < 1.0).is_true()\n\n\tawait key_up(KEY_SPACE)\n"
  },
  {
    "path": "tests/test_context_mapping_combine.gd.uid",
    "content": "uid://cbpfeigw63gdx\n"
  },
  {
    "path": "tests/test_context_switching.gd",
    "content": "extends GUIDETestBase\n\nvar _context1: GUIDEMappingContext\nvar _context2: GUIDEMappingContext\nvar _action: GUIDEAction\n\n\nfunc _setup() -> void:\n\t_context1 = mapping_context()\n\t_context2 = mapping_context()\n\t_action = action_bool(\"some_action\")\n\n\nfunc test_action_with_pressed_trigger_does_not_trigger_again_on_context_switch() -> void:\n\tvar input := input_key(KEY_A)\n\tvar trigger := trigger_pressed()\n\tmap(_context1, _action, input, [], [trigger] )\n\tmap(_context2, _action, input, [], [trigger] )\n\n\tGUIDE.enable_mapping_context(_context1)\n\tvar watched := watch(_action)\n\t# when i press the key\n\tkey_down(KEY_A, true)\n\n\t# then the action is triggered\n\tawait watched.assert_triggered()\n\n\tawait wait_f(10)\n\n\t# since we have a pressed trigger, the action should not trigger again\n\twatched.assert_not_triggered()\n\n\t# when i switch to the second context\n\tGUIDE.enable_mapping_context(_context2)\n\n\t# then this should not trigger the action again, as the key is still down\n\twatched.assert_not_triggered()\n\n\nfunc test_action_with_down_trigger_triggers_again_on_context_switch() -> void:\n\tvar input := input_key(KEY_A)\n\tvar trigger := trigger_down()\n\tmap(_context1, _action, input, [], [trigger] )\n\tmap(_context2, _action, input, [], [trigger] )\n\n\tGUIDE.enable_mapping_context(_context1)\n\tvar watched := watch(_action)\n\t# when i press the key\n\tkey_down(KEY_A, true)\n\n\t# then the action is triggered\n\tawait watched.assert_triggered()\n\n\tawait wait_f(10)\n\n\t# since we have a down trigger, the action should trigger again\n\tawait watched.assert_triggered()\n\n\t# when i switch to the second context\n\tGUIDE.enable_mapping_context(_context2)\n\n\t# then this should trigger the action again\n\tawait watched.assert_triggered()\n\n\tawait wait_f(10)\n\n\t# and should still do so\n\tawait watched.assert_triggered()\n\n\t\nfunc test_action_with_different_inputs_retains_value_on_context_switch() -> void:\n\t_action = action_2d(\"some_action\")\n\t\n\t# we have 2 contexts with the same action, but different inputs (e.g. one keyboard, one gamepad)\n\tvar input1 : GUIDEInput = input_mouse_position()\t\n\tvar input2 : GUIDEInput = input_joy_axis_2d(JOY_AXIS_LEFT_X, JOY_AXIS_LEFT_Y)\n\tmap(_context1, _action, input1)\n\tmap(_context2, _action, input2, [modifier_virtual_cursor()])\n\t\n\tGUIDE.enable_mapping_context(_context1)\n\t\n\t# wait 1 frame\n\tawait wait_f(1)\n\t\n\tassert_vector(_action.value_axis_2d).is_equal_approx(Vector2(get_viewport().get_mouse_position()), Vector2(0.01, 0.01))\n\t\n\t\n\t# if we now switch the mapping contexts \n\tGUIDE.enable_mapping_context(_context2, true)\n\t\n\t# the value should now be the value of the virtual cursor\n\t# without waiting for a frame.\n\tassert_vector(_action.value_axis_2d).is_equal_approx(Vector2(get_viewport().get_visible_rect().size / 2.0), Vector2(0.01, 0.01))\n\n\nfunc test_enable_mapping_context_emits_signals() -> void:\n\t# Set up an enabled signal watcher\n\tvar enabled_count := [0]\n\t_context1.enabled.connect(func() -> void: enabled_count[0] += 1)\n\n\t# Enable context1\n\tGUIDE.enable_mapping_context(_context1)\n\t\n\t# Signal should be triggered\n\tassert_int(enabled_count[0]).is_equal(1)\n\tenabled_count[0] = 0\n\n\t# Set up a disabled signal watcher\n\tvar disabled_count := [0]\n\t_context1.disabled.connect(func() -> void: disabled_count[0] += 1)\n\n\t# Enable context2 and disable others\n\tGUIDE.enable_mapping_context(_context2, true)\n\t\n\t# Disabled signal should be emitted for context1\n\tassert_int(enabled_count[0]).is_equal(0)\n\tassert_int(disabled_count[0]).is_equal(1)\n\n\nfunc test_disable_mapping_context_emits_signals() -> void:\n\t# Enable context1\n\tGUIDE.enable_mapping_context(_context1)\n\n\t# Set up a disabled signal watcher\n\tvar disabled_count := [0]\n\t_context1.disabled.connect(func() -> void: disabled_count[0] += 1)\n\t\n\t# Disable context1\n\tGUIDE.disable_mapping_context(_context1)\n\n\t# Disabled signal should be emitted for context1\n\tassert_int(disabled_count[0]).is_equal(1)\n\n\nfunc test_set_enabled_mapping_contexts_returns_previous_contexts() -> void:\n\t# Given some active contexts\n\tGUIDE.enable_mapping_context(_context1)\n\tGUIDE.enable_mapping_context(_context2)\n\n\t# When we set new contexts\n\tvar context3 := mapping_context()\n\tvar previous := GUIDE.set_enabled_mapping_contexts([context3])\n\n\t# Then the previous contexts are returned\n\tassert_array(previous).contains_exactly([_context1, _context2])\n\n\nfunc test_set_enabled_mapping_contexts_activates_new_contexts() -> void:\n\t# Given some active contexts\n\tGUIDE.enable_mapping_context(_context1)\n\n\t# When we set new contexts\n\tvar context3 := mapping_context()\n\tGUIDE.set_enabled_mapping_contexts([_context2, context3])\n\n\t# Then the new contexts are active\n\tassert_bool(GUIDE.is_mapping_context_enabled(_context2)).is_true()\n\tassert_bool(GUIDE.is_mapping_context_enabled(context3)).is_true()\n\n\nfunc test_set_enabled_mapping_contexts_deactivates_old_contexts() -> void:\n\t# Given some active contexts\n\tGUIDE.enable_mapping_context(_context1)\n\tGUIDE.enable_mapping_context(_context2)\n\n\t# When we set new contexts that don't include context1\n\tvar context3 := mapping_context()\n\tGUIDE.set_enabled_mapping_contexts([_context2, context3])\n\n\t# Then context1 is no longer active\n\tassert_bool(GUIDE.is_mapping_context_enabled(_context1)).is_false()\n\n\nfunc test_set_enabled_mapping_contexts_with_empty_array_clears_all() -> void:\n\t# Given some active contexts\n\tGUIDE.enable_mapping_context(_context1)\n\tGUIDE.enable_mapping_context(_context2)\n\n\t# When we set an empty array\n\tvar previous := GUIDE.set_enabled_mapping_contexts([])\n\n\t# Then all contexts are deactivated\n\tassert_bool(GUIDE.is_mapping_context_enabled(_context1)).is_false()\n\tassert_bool(GUIDE.is_mapping_context_enabled(_context2)).is_false()\n\t# And previous contexts are returned\n\tassert_array(previous).contains_exactly([_context1, _context2])\n\n\nfunc test_set_enabled_mapping_contexts_with_single_context() -> void:\n\t# When we set a single context\n\tGUIDE.set_enabled_mapping_contexts([_context1])\n\n\t# Then only that context is active\n\tassert_bool(GUIDE.is_mapping_context_enabled(_context1)).is_true()\n\tassert_bool(GUIDE.is_mapping_context_enabled(_context2)).is_false()\n\n\nfunc test_set_enabled_mapping_contexts_emits_enabled_signals() -> void:\n\t# Given we watch for enabled signals\n\tvar enabled1 := [false]\n\tvar enabled2 := [false]\n\t_context1.enabled.connect(func() -> void: enabled1[0] = true)\n\t_context2.enabled.connect(func() -> void: enabled2[0] = true)\n\n\t# When we set new contexts\n\tGUIDE.set_enabled_mapping_contexts([_context1, _context2])\n\n\t# Then enabled signals are emitted for all new contexts\n\tassert_bool(enabled1[0]).is_true()\n\tassert_bool(enabled2[0]).is_true()\n\n\nfunc test_set_enabled_mapping_contexts_emits_disabled_signals() -> void:\n\t# Given some active contexts with disabled signal watchers\n\tGUIDE.enable_mapping_context(_context1)\n\tGUIDE.enable_mapping_context(_context2)\n\tvar disabled1 := [false]\n\tvar disabled2 := [false]\n\t_context1.disabled.connect(func() -> void: disabled1[0] = true)\n\t_context2.disabled.connect(func() -> void: disabled2[0] = true)\n\n\t# When we set new contexts that don't include the old ones\n\tvar context3 := mapping_context()\n\tGUIDE.set_enabled_mapping_contexts([context3])\n\n\t# Then disabled signals are emitted for removed contexts\n\tassert_bool(disabled1[0]).is_true()\n\tassert_bool(disabled2[0]).is_true()\n\n\nfunc test_set_enabled_mapping_contexts_later_contexts_have_higher_precedence() -> void:\n\t# Given an action mapped to different inputs in two contexts\n\tvar input1 := input_key(KEY_A)\n\tvar input2 := input_key(KEY_B)\n\tmap(_context1, _action, input1)\n\tmap(_context2, _action, input2)\n\n\t# When we set contexts with context2 later in the array\n\tGUIDE.set_enabled_mapping_contexts([_context1, _context2])\n\n\tvar watched := watch(_action)\n\n\t# When we press input2 (from context2)\n\tkey_down(KEY_B, true)\n\n\t# Then the action is triggered (context2 has precedence)\n\tawait watched.assert_triggered()\n\n\t# And input1 does not trigger (because context2's input takes precedence due to later timestamp)\n\tkey_up(KEY_B, true)\n\tawait wait_f(10)\n\twatched.reset()\n\tkey_down(KEY_A, true)\n\n\t# Context1's input should still work, just with lower precedence\n\tawait watched.assert_triggered()\n\n\nfunc test_set_enabled_mapping_contexts_with_duplicate_contexts_uses_later_occurrence() -> void:\n\t# When we set contexts with duplicates\n\tGUIDE.set_enabled_mapping_contexts([_context1, _context2, _context1])\n\n\t# Then the context is still enabled (later occurrence wins)\n\tassert_bool(GUIDE.is_mapping_context_enabled(_context1)).is_true()\n\tassert_bool(GUIDE.is_mapping_context_enabled(_context2)).is_true()\n\n\t# And there should be only 2 contexts active (not 3)\n\tvar active := GUIDE.get_enabled_mapping_contexts()\n\tassert_int(active.size()).is_equal(2)\n\n\nfunc test_set_enabled_mapping_contexts_does_not_emit_enabled_for_already_active_contexts() -> void:\n\t# Given context1 is already enabled\n\tGUIDE.enable_mapping_context(_context1)\n\n\t# And we set up a signal watcher\n\tvar enabled_count := [0]\n\t_context1.enabled.connect(func() -> void: enabled_count[0] += 1)\n\n\t# When we set contexts that include context1 (which is already active)\n\tGUIDE.set_enabled_mapping_contexts([_context1, _context2])\n\n\t# Then the enabled signal should NOT be emitted for context1 (it was already enabled)\n\tassert_int(enabled_count[0]).is_equal(0)\n\n\nfunc test_set_enabled_mapping_contexts_does_not_emit_disabled_for_contexts_that_were_enabled() -> void:\n\t# Given context1 and context2 are already enabled\n\tGUIDE.set_enabled_mapping_contexts([_context1, _context2])\n\n\t# And we set up a signal watcher\n\tvar disabled_count := [0]\n\t_context1.disabled.connect(func() -> void: disabled_count[0] += 1)\n\n\t# When we set context to context1 (which is already active)\n\tGUIDE.set_enabled_mapping_contexts([_context1])\n\n\t# Then the disabled signal should NOT be emitted for context1 (it was already enabled)\n\tassert_int(disabled_count[0]).is_equal(0)\n\n\nfunc test_set_enabled_mapping_contexts_emits_enabled_only_for_new_contexts() -> void:\n\t# Given context1 is already enabled\n\tGUIDE.enable_mapping_context(_context1)\n\n\t# And we set up signal watchers\n\tvar enabled1_count := [0]\n\tvar enabled2_count := [0]\n\t_context1.enabled.connect(func() -> void: enabled1_count[0] += 1)\n\t_context2.enabled.connect(func() -> void: enabled2_count[0] += 1)\n\n\t# When we set contexts that include both context1 (already active) and context2 (new)\n\tGUIDE.set_enabled_mapping_contexts([_context1, _context2])\n\n\t# Then enabled should only be emitted for context2 (the new one)\n\tassert_int(enabled1_count[0]).is_equal(0)\n\tassert_int(enabled2_count[0]).is_equal(1)\n\n"
  },
  {
    "path": "tests/test_context_switching.gd.uid",
    "content": "uid://bwuqv5wka2jsq\n"
  },
  {
    "path": "tests/test_down_trigger.gd",
    "content": "extends GUIDETestBase\n\nvar _context:GUIDEMappingContext\nvar _action:GUIDEAction\nvar _trigger:GUIDETrigger\n\nfunc _setup() -> void:\n\t_context = mapping_context()\n\t_action = action_bool()\n\t_trigger = trigger_down()\n\nfunc test_down_trigger_works_for_key_input() -> void:\n\tvar input := input_key(KEY_Q)\n\tmap(_context, _action, input, [], [_trigger])\n\tGUIDE.enable_mapping_context(_context)\n\t\n\t# WHEN: i press the key\n\tawait key_down(KEY_Q)\n\n\t# THEN: the action is triggered\n\tassert_bool(_action.is_triggered()).is_true()\n\t\n\t# WHEN: i release the key\n\tawait key_up(KEY_Q)\n\t\n\t# THEN: the action is not triggered anymore\n\tassert_bool(_action.is_triggered()).is_false()\n\t\n\n\n\n"
  },
  {
    "path": "tests/test_down_trigger.gd.uid",
    "content": "uid://ctti46nq73a2u\n"
  },
  {
    "path": "tests/test_guide_default_text_provider.gd",
    "content": "## Tests the GUIDE default text provider rendering for all supported input types.\n## Ensures GUIDEInputFormatter.input_as_text returns the expected string for each input.\nextends GUIDETestBase\n\nvar _formatter: GUIDEInputFormatter\n\n# The meta key label is OS specific (e.g. Windows, Cmd, Meta), \n# so we cannot hardcode it.\nstatic var meta_key_label:String = OS.get_keycode_string(KEY_META)\n\t\n# we make a high priority default provider so it doesn't matter which device\n# is currently connected, we always get the default strings.\t\nclass DefaultProvider:\n\textends GUIDEInputFormatter.DefaultTextProvider\n\t\n\tfunc _init() -> void:\n\t\tsuper()\n\t\tpriority = -100\t\n\t\n\t\nfunc _setup() -> void:\n\t_formatter = auto_free(GUIDEInputFormatter.new())\n\tGUIDEInputFormatter.add_text_provider(DefaultProvider.new())\n\t\n\t\n## Keyboard input tests\n@warning_ignore(\"unused_parameter\")\nfunc test_key_input_renders_special_keys_with_modifiers(\n\tkey_code: int, control: bool, alt: bool, shift: bool, meta: bool, expected: String,\n\ttest_parameters := [\n\t\t[KEY_A, false, false, false, false, \"[A]\"],\n\t\t[KEY_A, true, false, false, false, \"[Ctrl] + [A]\"],\n\t\t[KEY_A, false, true, false, false, \"[Alt] + [A]\"],\n\t\t[KEY_A, false, false, true, false, \"[Shift] + [A]\"],\n\t\t[KEY_A, false, false, false, true, \"[\" + meta_key_label + \"] + [A]\"],\n\t\t[KEY_A, true, true, true, true, \"[Ctrl] + [Alt] + [Shift] + [\" +  meta_key_label + \"] + [A]\"],\n\t\t[KEY_ENTER, false, false, false, false, \"[Enter]\"],\n\t\t[KEY_ENTER, true, false, false, false, \"[Ctrl] + [Enter]\"],\n\t\t[KEY_SHIFT, false, false, false, false, \"[Shift]\"],\n\t\t[KEY_ESCAPE, false, false, false, false, \"[Escape]\"],\n\t\t[KEY_ESCAPE, false, true, false, false, \"[Alt] + [Escape]\"]\n\t]\n) -> void:\n\tvar input: GUIDEInputKey = input_key(key_code)\n\tinput.control = control\n\tinput.alt = alt\n\tinput.shift = shift\n\tinput.meta = meta\n\tvar result: String = _formatter.input_as_text(input)\n\tassert_str(result).is_equal(expected)\n\n## Mouse button input tests\n@warning_ignore(\"unused_parameter\")\nfunc test_mouse_button_input_renders_all_buttons(\n\tbutton_code: int, expected: String,\n\ttest_parameters := [\n\t\t[MOUSE_BUTTON_LEFT, \"[Left Mouse Button]\"],\n\t\t[MOUSE_BUTTON_RIGHT, \"[Right Mouse Button]\"],\n\t\t[MOUSE_BUTTON_MIDDLE, \"[Middle Mouse Button]\"],\n\t\t[MOUSE_BUTTON_WHEEL_UP, \"[Mouse Wheel Up]\"],\n\t\t[MOUSE_BUTTON_WHEEL_DOWN, \"[Mouse Wheel Down]\"],\n\t\t[MOUSE_BUTTON_WHEEL_LEFT, \"[Mouse Wheel Left]\"],\n\t\t[MOUSE_BUTTON_WHEEL_RIGHT, \"[Mouse Wheel Right]\"],\n\t\t[MOUSE_BUTTON_XBUTTON1, \"[Mouse Side 1]\"],\n\t\t[MOUSE_BUTTON_XBUTTON2, \"[Mouse Side 2]\"]\n\t]\n) -> void:\n\tvar input: GUIDEInputMouseButton = input_mouse_button(button_code)\n\tvar result: String = _formatter.input_as_text(input)\n\tassert_str(result).is_equal(expected)\n\n## Mouse axis input tests\n@warning_ignore(\"unused_parameter\")\nfunc test_mouse_axis_1d_input_renders_all_axes(\n\taxis_code: int, expected: String,\n\ttest_parameters := [\n\t\t[GUIDEInputMouseAxis1D.GUIDEInputMouseAxis.X, \"[Mouse Left/Right]\"],\n\t\t[GUIDEInputMouseAxis1D.GUIDEInputMouseAxis.Y, \"[Mouse Up/Down]\"]\n\t]\n) -> void:\n\tvar input: GUIDEInputMouseAxis1D = input_mouse_axis_1d(axis_code)\n\tvar result: String = _formatter.input_as_text(input)\n\tassert_str(result).is_equal(expected)\n\n## Joy button input tests\n@warning_ignore(\"unused_parameter\")\nfunc test_joy_button_input_renders_all_buttons(\n\tbutton_code: int, expected: String,\n\ttest_parameters := [\n\t\t[JOY_BUTTON_A, \"[Joy 0]\"],\n\t\t[JOY_BUTTON_B, \"[Joy 1]\"],\n\t\t[JOY_BUTTON_X, \"[Joy 2]\"],\n\t\t[JOY_BUTTON_Y, \"[Joy 3]\"]\n\t]\n) -> void:\n\tvar input: GUIDEInputJoyButton = input_joy_button(button_code)\n\tvar result: String = _formatter.input_as_text(input)\n\tassert_str(result).is_equal(expected)\n\n## Joy axis input tests\n@warning_ignore(\"unused_parameter\")\nfunc test_joy_axis_1d_input_renders_all_axes(\n\taxis_code: int, expected: String,\n\ttest_parameters := [\n\t\t[JOY_AXIS_LEFT_X, \"[Stick 1 Horizontal]\"],\n\t\t[JOY_AXIS_LEFT_Y, \"[Stick 1 Vertical]\"],\n\t\t[JOY_AXIS_RIGHT_X, \"[Stick 2 Horizontal]\"],\n\t\t[JOY_AXIS_RIGHT_Y, \"[Stick 2 Vertical]\"],\n\t\t[JOY_AXIS_TRIGGER_LEFT, \"[Axis 3]\"],\n\t\t[JOY_AXIS_TRIGGER_RIGHT, \"[Axis 4]\"]\n\t]\n) -> void:\n\tvar input: GUIDEInputJoyAxis1D = input_joy_axis_1d(axis_code)\n\tvar result: String = _formatter.input_as_text(input)\n\tassert_str(result).is_equal(expected)\n\n## Joy axis input tests\n@warning_ignore(\"unused_parameter\")\nfunc test_joy_axis_2d_input_renders_all_axes(\n\tx_axis: int, y_axis: int, expected: String,\n\ttest_parameters := [\n\t\t[JOY_AXIS_LEFT_X, JOY_AXIS_LEFT_Y, \"[Stick 1]\"],\n\t\t[JOY_AXIS_RIGHT_X, JOY_AXIS_RIGHT_Y, \"[Stick 2]\"]\n\t]\n) -> void:\n\tvar input: GUIDEInputJoyAxis2D = input_joy_axis_2d(x_axis, y_axis)\n\tvar result: String = _formatter.input_as_text(input)\n\tassert_str(result).is_equal(expected)\n\n\n## Mouse axis 2D input tests\n@warning_ignore(\"unused_parameter\")\nfunc test_mouse_axis_2d_input_renders_all() -> void:\n\tvar input: GUIDEInputMouseAxis2D = input_mouse_axis_2d()\n\tvar result: String = _formatter.input_as_text(input)\n\tassert_str(result).is_equal(\"[Mouse]\")\n\n## Mouse position input tests\n@warning_ignore(\"unused_parameter\")\nfunc test_mouse_position_input_renders_all() -> void:\n\tvar input: GUIDEInputMousePosition = input_mouse_position()\n\tvar result: String = _formatter.input_as_text(input)\n\tassert_str(result).is_equal(\"[Mouse Position]\")\n\n## Touch angle input tests\n@warning_ignore(\"unused_parameter\")\nfunc test_touch_angle_input_renders_all() -> void:\n\tvar input: GUIDEInputTouchAngle = input_touch_angle()\n\tvar result: String = _formatter.input_as_text(input)\n\tassert_str(result).is_equal(\"[Touch Angle]\")\n\n## Touch distance input tests\n@warning_ignore(\"unused_parameter\")\nfunc test_touch_distance_input_renders_all() -> void:\n\tvar input: GUIDEInputTouchDistance = input_touch_distance()\n\tvar result: String = _formatter.input_as_text(input)\n\tassert_str(result).is_equal(\"[Touch Distance]\")\n\n## Touch axis 1D input tests\n@warning_ignore(\"unused_parameter\")\nfunc test_touch_axis_1d_input_renders_all_axes(\n\taxis_code: int, finger_index: int, expected: String,\n\ttest_parameters := [\n\t\t[GUIDEInputTouchAxis1D.GUIDEInputTouchAxis.X, 0, \"[Touch Left/Right 0]\"],\n\t\t[GUIDEInputTouchAxis1D.GUIDEInputTouchAxis.X, -1, \"[Touch Left/Right Average]\"],\n\t\t[GUIDEInputTouchAxis1D.GUIDEInputTouchAxis.Y, 1, \"[Touch Up/Down 1]\"],\n\t\t[GUIDEInputTouchAxis1D.GUIDEInputTouchAxis.Y, -1, \"[Touch Up/Down Average]\"]\n\t]\n) -> void:\n\tvar input: GUIDEInputTouchAxis1D = input_touch_axis_1d(axis_code, finger_index)\n\tvar result: String = _formatter.input_as_text(input)\n\tassert_str(result).is_equal(expected)\n\n## Touch axis 2D input tests\n@warning_ignore(\"unused_parameter\")\nfunc test_touch_axis_2d_input_renders_all(\n\tfinger_index: int, expected: String,\n\ttest_parameters := [\n\t\t[0, \"[Touch Axis 2D 0]\"],\n\t\t[-1, \"[Touch Axis 2D Average]\"]\n\t]\n) -> void:\n\tvar input: GUIDEInputTouchAxis2D = input_touch_axis_2d(finger_index)\n\tvar result: String = _formatter.input_as_text(input)\n\tassert_str(result).is_equal(expected)\n\n\n## Any input tests\n@warning_ignore(\"unused_parameter\")\nfunc test_any_input_renders_all(\n\tjoy: bool, mouse: bool, keyboard: bool, expected: String,\n\ttest_parameters := [\n\t\t[true, false, false, \"[Any Joy Button]\"],\n\t\t[false, true, false, \"[Any Mouse Button]\"],\n\t\t[false, false, true, \"[Any Key]\"],\n\t\t[true, true, true, \"[Any Joy Button/Mouse Button/Key]\"]\n\t]\n) -> void:\n\tvar input: GUIDEInputAny = input_any()\n\tinput.joy = joy\n\tinput.mouse = mouse\n\tinput.keyboard = keyboard\n\tvar result: String = _formatter.input_as_text(input)\n\tassert_str(result).is_equal(expected)\n\n"
  },
  {
    "path": "tests/test_guide_default_text_provider.gd.uid",
    "content": "uid://co2bupe8ngjue\n"
  },
  {
    "path": "tests/test_guide_input_formatter.gd",
    "content": "# Tests the input formatter.\nextends GUIDETestBase\n\n# we make a high priority default provider so it doesn't matter which device\n# is currently connected, we always get the default strings.\t\nclass DefaultProvider:\n\textends GUIDEInputFormatter.DefaultTextProvider\n\t\n\tfunc _init() -> void:\n\t\tsuper()\n\t\tpriority = -100\t\n\t\nvar _context:GUIDEMappingContext\nvar _action:GUIDEAction\nvar _formatter:GUIDEInputFormatter\n\n\nfunc _setup() -> void:\n\t_formatter = GUIDEInputFormatter.for_active_contexts()\n\tGUIDEInputFormatter.add_text_provider(DefaultProvider.new())\n\t_context = mapping_context()\n\t_action = action_bool()\t\n\t\n\t\nfunc _filter_keyboard() -> void:\n\t_formatter.formatting_options.input_filter = func(context:GUIDEInputFormatter.FormattingContext) -> bool:\n\t\t# only show keyboard input\n\t\treturn context.input.device_type & GUIDEInput.DeviceType.KEYBOARD > 0\n\nfunc _filter_joy() -> void:\n\t_formatter.formatting_options.input_filter = func(context:GUIDEInputFormatter.FormattingContext) -> bool:\n\t\t# only show keyboard input\n\t\treturn context.input.device_type & GUIDEInput.DeviceType.JOY > 0\n\t\nfunc test_simple_input() -> void:\n\tmap(_context, _action, input_key(KEY_A))\n\tGUIDE.enable_mapping_context(_context)\n\t\n\tvar result:String = _formatter.action_as_text(_action)\n\tassert_str(result).is_equal(\"[A]\")\n\t\n\t\nfunc test_multi_binding() -> void:\n\tmap(_context, _action, input_key(KEY_A))\n\tmap(_context, _action, input_key(KEY_B))\n\t\n\tGUIDE.enable_mapping_context(_context)\n\t\n\tvar result:String = _formatter.action_as_text(_action)\n\tassert_str(result).is_equal(\"[A], [B]\")\n\nfunc test_combo() -> void:\n\tvar combo_a := action_bool()\n\tvar combo_b := action_bool()\n\tmap(_context, combo_a, input_key(KEY_A))\n\tmap(_context, combo_b, input_key(KEY_B))\n\tmap(_context, _action, null, [], [trigger_combo([combo_a, combo_b])])\n\n\tGUIDE.enable_mapping_context(_context)\n\t\t\n\tvar result:String = _formatter.action_as_text(_action)\n\tassert_str(result).is_equal(\"[A] > [B]\")\n\t\nfunc test_chorded() -> void:\n\tvar chord := action_bool()\n\tmap(_context, chord, input_key(KEY_A))\n\tmap(_context, _action, input_key(KEY_B), [], [trigger_chorded_action(chord)])\n\t\n\tGUIDE.enable_mapping_context(_context)\n\t\n\tvar result:String = _formatter.action_as_text(_action)\n\tassert_str(result).is_equal(\"[A] + [B]\")\n\t\n\t\nfunc test_type_filter_simple() -> void:\n\tmap(_context, _action, input_key(KEY_A))\n\tmap(_context, _action, input_joy_button(JOY_BUTTON_B))\n\tGUIDE.enable_mapping_context(_context)\n\t\n\tvar result:String = _formatter.action_as_text(_action)\n\tassert_str(result).is_equal(\"[A], [Joy 1]\")\n\t\n\t_filter_keyboard()\n\t\n\tresult = _formatter.action_as_text(_action)\n\tassert_str(result).is_equal(\"[A]\")\n\n\t_filter_joy()\n\t\n\tresult = _formatter.action_as_text(_action)\n\tassert_str(result).is_equal(\"[Joy 1]\")\n\n\nfunc test_type_filter_chord() -> void:\n\tvar chord := action_bool()\n\tmap(_context, chord, input_key(KEY_SHIFT))\n\tmap(_context, chord, input_joy_button(JOY_BUTTON_LEFT_SHOULDER))\n\t\n\t\n\tmap(_context, _action, input_key(KEY_A), [], [trigger_chorded_action(chord)])\n\tmap(_context, _action, input_joy_button(JOY_BUTTON_B), [], [trigger_chorded_action(chord)])\n\tGUIDE.enable_mapping_context(_context)\n\t\n\tvar result:String = _formatter.action_as_text(_action)\n\tassert_str(result).is_equal(\"[Shift], [Joy 9] + [A], [Shift], [Joy 9] + [Joy 1]\")\n\t\n\t_filter_keyboard()\n\t\n\tresult = _formatter.action_as_text(_action)\n\tassert_str(result).is_equal(\"[Shift] + [A]\")\n\n\t_filter_joy()\n\t\n\tresult = _formatter.action_as_text(_action)\n\tassert_str(result).is_equal(\"[Joy 9] + [Joy 1]\")\n\n\n\nfunc test_type_filter_empty() -> void:\n\tmap(_context, _action, input_key(KEY_A))\n\tGUIDE.enable_mapping_context(_context)\n\n\t_filter_joy()\n\t\n\tvar result:String = _formatter.action_as_text(_action)\n\tassert_str(result).is_equal(\"\")\n"
  },
  {
    "path": "tests/test_guide_input_formatter.gd.uid",
    "content": "uid://bpyafcpcmohi7\n"
  },
  {
    "path": "tests/test_input_detector.gd",
    "content": "extends GUIDETestBase\n\nvar _input_detector: GUIDEInputDetector\n\nfunc _setup() -> void:\n\t_input_detector = auto_free(GUIDEInputDetector.new())\n\t_input_detector.detection_countdown_seconds = 0\n\t_input_detector.detection_started.connect(func() -> void: print_f(\"Detection started.\"))\n\t_input_detector.input_detected.connect(func(val:GUIDEInput) -> void: print_f(\"Input detected: %s\" % val))\n\t\n\tadd_child(_input_detector)\n\t\n\nfunc test_input_is_detected() -> void:\n\tmonitor_signals(_input_detector)\n\t\n\t# when i try to detect boolean input\n\t_input_detector.detect(GUIDEAction.GUIDEActionValueType.BOOL)\n\t\n\t# and i press a key\n\ttap_key(KEY_A)\n\t\n\t# then the input detector should emit a signal with the detected input\n\tawait assert_signal(_input_detector).is_emitted(\"input_detected\", [GUIDEInputMatcher.new(input_key(KEY_A))])\n\t\n\nfunc test_axis_1d_input_is_detected() -> void:\n\tmonitor_signals(_input_detector)\n\t\n\t# when i try to detect axis 1D input\n\t_input_detector.detect(GUIDEAction.GUIDEActionValueType.AXIS_1D)\n\t\n\t# and i move the mouse horizontally\n\tawait mouse_move_by(Vector2(20, 0))\n\t\n\t# then the input detector should emit a signal with the detected input\n\tawait assert_signal(_input_detector).is_emitted(\"input_detected\", [GUIDEInputMatcher.new(input_mouse_axis_1d(GUIDEInputMouseAxis1D.GUIDEInputMouseAxis.X))])\n\t\n\t\n\nfunc test_axis_2d_input_is_detected() -> void:\n\tmonitor_signals(_input_detector)\n\t\n\t# when i try to detect axis 2D input\n\t_input_detector.detect(GUIDEAction.GUIDEActionValueType.AXIS_2D)\n\t\n\t# and i move the mouse diagonally\n\tawait mouse_move_by(Vector2(20, 20))\n\t\n\t# then the input detector should emit a signal with the detected input\n\tawait assert_signal(_input_detector).is_emitted(\"input_detected\", [GUIDEInputMatcher.new(input_mouse_axis_2d())])\n\n\t\nfunc test_aborting_input_detection_works() -> void:\n\tmonitor_signals(_input_detector)\n\t_input_detector.abort_detection_on = [input_key(KEY_ESCAPE)]\n\t\n\t# when i try to detect boolean input\n\t_input_detector.detect(GUIDEAction.GUIDEActionValueType.BOOL)\n\t\n\t# and i press the abort key\n\ttap_key(KEY_ESCAPE)\n\t\n\t# then the input detector should emit a signal with null as the detected input\n\tawait assert_signal(_input_detector).is_emitted(\"input_detected\", [null])\n\t\n\nfunc test_input_detector_ensures_that_abort_input_is_released_before_detection() -> void:\n\tmonitor_signals(_input_detector)\n\t_input_detector.abort_detection_on = [input_key(KEY_ESCAPE)]\n\tvar holder:Array = []\n\t# when i start pressing the abort key\n\tawait key_down(KEY_ESCAPE)\n\t\n\t# and then run an input detection\n\tvar detect := func() -> void:\n\t\t_input_detector.detect(GUIDEAction.GUIDEActionValueType.BOOL) \n\t\tholder.append(await _input_detector.input_detected)\n\t\n\tdetect.call()\n\t\n\t# and then release the abort key\n\tawait key_up(KEY_ESCAPE)\n\t\n\t# and then send a normal escape\n\tawait tap_key(KEY_ESCAPE)\n\t\n\t# then the input detector should emit a signal with null as the detected input\n\tassert_array(holder).has_size(1)\n\tassert_object(holder[0]).is_null()\n"
  },
  {
    "path": "tests/test_input_detector.gd.uid",
    "content": "uid://d1u547ndj8uvg\n"
  },
  {
    "path": "tests/test_input_on_context_change.gd",
    "content": "extends GUIDETestBase\n\n\nvar _context1:GUIDEMappingContext\nvar _context2:GUIDEMappingContext\nvar _action1:GUIDEAction\nvar _action2:GUIDEAction\n\nfunc _setup() -> void:\n\t_context1 = mapping_context()\n\t_context2 = mapping_context()\n\t_action1 = action_bool()\n\t_action2 = action_bool()\n\n# https://github.com/godotneers/G.U.I.D.E/issues/61\n# Switching mapping contexts while an input is active should not deactivate the input.\nfunc test_input_stays_active_when_mapping_contexts_change() -> void:\n\tvar input:GUIDEInput = input_key(KEY_A)\n\tmap(_context1, _action1, input)\n\tmap(_context2, _action2, input)\n\n\tGUIDE.enable_mapping_context(_context1)\n\tvar watched1 := watch(_action1)\n\tvar watched2 := watch(_action2)\n\n\t# WHEN\n\t# i press the key down\n\tawait key_down(KEY_A)\n\n\t# THEN\n\t# the first action is triggered\n\tawait watched1.assert_triggered()\n\n\t# when I now switch the mapping context, but keep the key pressed\n\tGUIDE.enable_mapping_context(_context2, true)\n\n\t# THEN\n\t# the second action is triggered\n\tawait watched2.assert_triggered()\n"
  },
  {
    "path": "tests/test_input_on_context_change.gd.uid",
    "content": "uid://c0nb3kirx8102\n"
  },
  {
    "path": "tests/test_invalid_configuration.gd",
    "content": "extends GUIDETestBase\n\nvar _context:GUIDEMappingContext\nvar _action:GUIDEAction\n\nfunc _setup() -> void:\n\t_context = mapping_context()\n\t_action = action_bool()\n\nfunc test_missing_action_is_properly_handled() -> void:\n\t_context.resource_path = \"dummy://test_context\"\n\tvar input := input_any()\n\tinput.mouse = true\n\tmap(_context, null, input) # action is missing\n\t\n\t# when we enable the mapping context, we see a warning\n\tawait assert_error(func() -> void: \tGUIDE.enable_mapping_context(_context)) \\\n\t\t.is_push_warning(\"Mapping at position 1 in context dummy://test_context has no action set. This mapping will be ignored.\")\n\n\t\n\t\n\t\n"
  },
  {
    "path": "tests/test_invalid_configuration.gd.uid",
    "content": "uid://dvnlo4b1o7737\n"
  },
  {
    "path": "tests/test_joy_axis_1d_input.gd",
    "content": "extends GUIDETestBase\n\n\nvar _context:GUIDEMappingContext\nvar _action:GUIDEAction\n\nfunc _setup() -> void:\n\t_context = mapping_context()\n\t_action = action_1d()\n\nfunc test_joy_axis_1d_input() -> void:\n\tvar input := input_joy_axis_1d(JOY_AXIS_LEFT_X)\n\tmap(_context, _action, input)\n\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\t# WHEN\n\t# I actuate the left joystick\n\tawait joy_axis(JOY_AXIS_LEFT_X, -0.5)\n\n\t# THEN\n\t# the action is triggered\n\tawait watched.assert_triggered()\n\t\n\t# and the value is correct\n\tassert_float(_action.value_axis_1d).is_equal(-0.5)\n\t\t\nfunc test_joy_axis_1d_input_ignores_other_axis() -> void:\n\tvar input := input_joy_axis_1d(JOY_AXIS_LEFT_X)\n\tmap(_context, _action, input)\n\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\t# WHEN\n\t# I actuate the right joystick\n\tawait joy_axis(JOY_AXIS_LEFT_Y, -0.5)\n\n\t# THEN\n\t# the action is not triggered\n\twatched.assert_not_triggered()\n\t\n\t# and the value is unchanged\n\tassert_float(_action.value_axis_1d).is_equal(0.0)\n\t\n"
  },
  {
    "path": "tests/test_joy_axis_1d_input.gd.uid",
    "content": "uid://d184ttp3qlmen\n"
  },
  {
    "path": "tests/test_joy_axis_2d_input.gd",
    "content": "extends GUIDETestBase\n\n\nvar _context:GUIDEMappingContext\nvar _action:GUIDEAction\n\nfunc _setup() -> void:\n\t_context = mapping_context()\n\t_action = action_2d()\n\nfunc test_joy_axis_2d_input() -> void:\n\tvar input := input_joy_axis_2d(JOY_AXIS_LEFT_X, JOY_AXIS_LEFT_Y)\n\tmap(_context, _action, input)\n\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\t# WHEN\n\t# I actuate the left joystick\n\tjoy_axis(JOY_AXIS_LEFT_X, -0.5, false)\n\tawait joy_axis(JOY_AXIS_LEFT_Y, 0.5)\n\n\t# THEN\n\t# the action is triggered\n\tawait watched.assert_triggered()\n\t\n\t# and the value is correct\n\tassert_vector(_action.value_axis_2d).is_equal(Vector2(-0.5, 0.5))\n\t\n"
  },
  {
    "path": "tests/test_joy_axis_2d_input.gd.uid",
    "content": "uid://b64ksg4crg8e1\n"
  },
  {
    "path": "tests/test_joy_button_input.gd",
    "content": "extends GUIDETestBase\n\n\nvar _context:GUIDEMappingContext\nvar _action:GUIDEAction\n\nfunc _setup() -> void:\n\t_context = mapping_context()\n\t_action = action_bool()\n\nfunc test_joy_button_input() -> void:\n\tvar input := input_joy_button(JOY_BUTTON_A)\n\tmap(_context, _action, input)\n\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\t# WHEN\n\t# i press the joy button\n\tawait tap_joy_button(JOY_BUTTON_A)\n\n\t# THEN\n\t# the action is triggered\n\tawait watched.assert_triggered()\n\t\n"
  },
  {
    "path": "tests/test_joy_button_input.gd.uid",
    "content": "uid://dmmi15s5yykuy\n"
  },
  {
    "path": "tests/test_key_input.gd",
    "content": "extends GUIDETestBase\n\n# https://github.com/godotneers/G.U.I.D.E/issues/34\n# if modifier IS the bound key, disabling \"allow additional modifiers\"\n# does not prevent the action from triggering\n@warning_ignore(\"unused_parameter\")\nfunc test_modifiers(modifier:int, test_parameters := [[KEY_SHIFT], [KEY_CTRL], [KEY_META], [KEY_ALT]]) -> void:\n\tvar mc := mapping_context()\n\t@warning_ignore(\"shadowed_variable\")\n\tvar _action := action_bool()\n\tvar input := input_key(modifier)\n\tinput.allow_additional_modifiers = false\n\tmap(mc, _action, input)\n\n\tGUIDE.enable_mapping_context(mc)\n\tvar watched := watch(_action)\n\n\t# WHEN\n\tawait tap_key(modifier)\n\n\t# THEN\n\tawait watched.assert_triggered()\n\t\n\n# If bind something to a key and the modifier is down that is disallowed\n# the action is not triggered.\nfunc test_disallowed_modifiers_prevent_action() -> void:\n\tvar mc := mapping_context()\n\t@warning_ignore(\"shadowed_variable\")\n\tvar _action := action_bool()\n\tvar input := input_key(KEY_A)\n\tinput.control = true\n\tinput.allow_additional_modifiers = false\n\tmap(mc, _action, input)\n\n\tGUIDE.enable_mapping_context(mc)\n\tvar watched := watch(_action)\n\n\t# WHEN\n\tawait tap_keys([KEY_CTRL, KEY_SHIFT, KEY_A])\n\n\t# THEN - not triggered because shift was down in addition to ctrl\n\twatched.assert_not_triggered()\n\n\t# WHEN\n\tawait tap_keys([KEY_CTRL, KEY_A])\n\n\t# THEN - triggered because only ctrl + a were down\n\tawait watched.assert_triggered()\n\n"
  },
  {
    "path": "tests/test_key_input.gd.uid",
    "content": "uid://clyarl55sf2a3\n"
  },
  {
    "path": "tests/test_layered_contexts.gd",
    "content": "extends GUIDETestBase\n\nvar _context1:GUIDEMappingContext\nvar _context2:GUIDEMappingContext\nvar _action1:GUIDEAction\nvar _action2:GUIDEAction\n\nfunc _setup() -> void:\n\t_context1 = mapping_context()\n\t_context2 = mapping_context()\n\t_action1 = action_bool()\n\t_action2 = action_bool()\n\t\n\tmap(_context1, _action1, input_key(KEY_A))\n\tmap(_context2, _action2, input_key(KEY_B))\n\t\n\n## https://github.com/godotneers/G.U.I.D.E/issues/89\n## When layering contexts we should not get duplicate action mappings.\nfunc test_mapping_works() -> void:\n\tGUIDE.enable_mapping_context(_context1)\n\n\tassert_int(GUIDE._active_action_mappings.size()).is_equal(1)\n\t\n\t# when i add the second context\n\tGUIDE.enable_mapping_context(_context2)\n\t\n\tassert_int(GUIDE._active_action_mappings.size()).is_equal(2)\n\n\n## https://github.com/godotneers/G.U.I.D.E/issues/94\n## When layering contexts, the inputs are properly mapped.\nfunc test_mapping_inputs_works() -> void:\n\tGUIDE.enable_mapping_context(_context1)\n\tvar watched1 := watch(_action1)\n\tvar watched2 := watch(_action2)\n\n\t# when i press the A key\n\ttap_key(KEY_A)\n\n\t# then action1 is triggered\n\tawait watched1.assert_triggered()\n\n\t# when i add the second context\n\tGUIDE.enable_mapping_context(_context2)\n\n\t# i should be able to press both keys now and they should trigger their actions\n\ttap_key(KEY_A)\n\tawait watched1.assert_triggered()\n\ttap_key(KEY_B)\n\tawait watched2.assert_triggered()\n\n\n## When layering contexts, disabling one context should not affect the other.\nfunc test_disable_one_context() -> void:\n\t# enable both contexts\n\tGUIDE.enable_mapping_context(_context1)\n\tGUIDE.enable_mapping_context(_context2)\n\tvar watched1 := watch(_action1)\n\tvar watched2 := watch(_action2)\n\n\t# verify both inputs work\n\ttap_key(KEY_A)\n\tawait watched1.assert_triggered()\n\ttap_key(KEY_B)\n\tawait watched2.assert_triggered()\n\n\t# disable the second context\n\tGUIDE.disable_mapping_context(_context2)\n\n\t# the first context should still work\n\ttap_key(KEY_A)\n\tawait watched1.assert_triggered()\n\n\t# but the second context should not\n\ttap_key(KEY_B)\n\twatched2.assert_not_triggered()\n\t\n"
  },
  {
    "path": "tests/test_layered_contexts.gd.uid",
    "content": "uid://cpuk00r24heav\n"
  },
  {
    "path": "tests/test_magnitude_modifier.gd",
    "content": "extends GUIDETestBase\n\n\nvar _context:GUIDEMappingContext\nvar _action:GUIDEAction\n\nfunc _setup() -> void:\n\t_context = mapping_context()\n\t_action = action_1d()\n\t\n\nfunc test_modifier_returns_zero_for_center() -> void:\n\tvar input: GUIDEInputJoyAxis2D = input_joy_axis_2d(JOY_AXIS_LEFT_X, JOY_AXIS_LEFT_Y)\n\tvar modifier := GUIDEModifierMagnitude.new()\n\tmap(_context, _action, input, [modifier])\n\tGUIDE.enable_mapping_context(_context)\n\t\n\t# when the joystick is centered\n\tjoy_axis(JOY_AXIS_LEFT_X, 0.0, false)\n\tawait joy_axis(JOY_AXIS_LEFT_Y, 0.0)\n\t# then the action's value is 0\n\tassert_float(_action.value_axis_1d).is_equal_approx(0.0, 0.01)\n\n\nfunc test_modifier_returns_length_for_unit_axes() -> void:\n\tvar input: GUIDEInputJoyAxis2D = input_joy_axis_2d(JOY_AXIS_LEFT_X, JOY_AXIS_LEFT_Y)\n\tvar modifier := GUIDEModifierMagnitude.new()\n\tmap(_context, _action, input, [modifier])\n\tGUIDE.enable_mapping_context(_context)\n\t\n\t# when I move the joystick fully to the right\n\tjoy_axis(JOY_AXIS_LEFT_X, 1.0, false)\n\tawait joy_axis(JOY_AXIS_LEFT_Y, 0.0)\n\t# then the action's value is 1\n\tassert_float(_action.value_axis_1d).is_equal_approx(1.0, 0.01)\n\t\n\t# when I move the joystick fully up\n\tjoy_axis(JOY_AXIS_LEFT_X, 0.0, false)\n\tawait joy_axis(JOY_AXIS_LEFT_Y, 1.0)\n\t# then the action's value is 1\n\tassert_float(_action.value_axis_1d).is_equal_approx(1.0, 0.01)\n\n\nfunc test_modifier_returns_pythagorean_length() -> void:\n\tvar input: GUIDEInputJoyAxis2D = input_joy_axis_2d(JOY_AXIS_LEFT_X, JOY_AXIS_LEFT_Y)\n\tvar modifier := GUIDEModifierMagnitude.new()\n\tmap(_context, _action, input, [modifier])\n\tGUIDE.enable_mapping_context(_context)\n\t\n\t# when I move the joystick to the top-right corner\n\tjoy_axis(JOY_AXIS_LEFT_X, 1.0, false)\n\tawait joy_axis(JOY_AXIS_LEFT_Y, 1.0)\n\t# then the action's value is sqrt(2)\n\tassert_float(_action.value_axis_1d).is_equal_approx(sqrt(2.0), 0.01)\n\t\n\t# when I move the joystick to the bottom-left corner\n\tjoy_axis(JOY_AXIS_LEFT_X, -1.0, false)\n\tawait joy_axis(JOY_AXIS_LEFT_Y, -1.0)\n\t# then the action's value is still sqrt(2) because magnitude ignores direction\n\tassert_float(_action.value_axis_1d).is_equal_approx(sqrt(2.0), 0.01)\n\n"
  },
  {
    "path": "tests/test_magnitude_modifier.gd.uid",
    "content": "uid://b8onstbdxc3mf\n"
  },
  {
    "path": "tests/test_map_range_modifier.gd",
    "content": "extends GUIDETestBase\n\n\nvar _context:GUIDEMappingContext\nvar _action:GUIDEAction\n\nfunc _setup() -> void:\n\t_context = mapping_context()\n\t_action = action_1d()\n\t\n\t\nfunc test_modifier_works_if_both_ranges_are_ascending() -> void:\n\tvar input := input_joy_axis_1d(JOY_AXIS_LEFT_X)\n\tvar modifier := modifier_map_range(-1, 1, 0, 100)\n\tmap(_context, _action, input, [modifier])\n\tGUIDE.enable_mapping_context(_context)\n\t\n\t# when i move the joy fully to the left\n\tawait joy_axis(JOY_AXIS_LEFT_X, -1)\n\t# then the action's value is 0\n\tassert_float(_action.value_axis_1d).is_equal_approx(0, 0.01)\n\t\n\t# when i move the joy fully to the right\n\tawait joy_axis(JOY_AXIS_LEFT_X, 1)\n\t# then the action's value is 100\n\tassert_float(_action.value_axis_1d).is_equal_approx(100, 0.01)\n\n\t\nfunc test_modifier_works_if_output_range_is_descending() -> void:\n\tvar input := input_joy_axis_1d(JOY_AXIS_LEFT_X)\n\tvar modifier := modifier_map_range(-1, 1, 100, 0)\n\tmap(_context, _action, input, [modifier])\n\tGUIDE.enable_mapping_context(_context)\n\t\n\t# when i move the joy fully to the left\n\tawait joy_axis(JOY_AXIS_LEFT_X, -1)\n\t# then the action's value is 100\n\tassert_float(_action.value_axis_1d).is_equal_approx(100, 0.01)\n\t\n\t# when i move the joy fully to the right\n\tawait joy_axis(JOY_AXIS_LEFT_X, 1)\n\t# then the action's value is 0\n\tassert_float(_action.value_axis_1d).is_equal_approx(0, 0.01)\n\t\n\nfunc test_modifier_works_if_input_range_is_descending() -> void:\n\tvar input := input_joy_axis_1d(JOY_AXIS_LEFT_X)\n\tvar modifier := modifier_map_range(1, -1, 0, 100)\n\tmap(_context, _action, input, [modifier])\n\tGUIDE.enable_mapping_context(_context)\n\t\n\t# when i move the joy fully to the left\n\tawait joy_axis(JOY_AXIS_LEFT_X, -1)\n\t# then the action's value is 100\n\tassert_float(_action.value_axis_1d).is_equal_approx(100, 0.01)\n\t\n\t# when i move the joy fully to the right\n\tawait joy_axis(JOY_AXIS_LEFT_X, 1)\n\t# then the action's value is 0\n\tassert_float(_action.value_axis_1d).is_equal_approx(0, 0.01)\n\t\n\nfunc test_modifier_works_if_both_ranges_are_descending() -> void:\n\tvar input := input_joy_axis_1d(JOY_AXIS_LEFT_X)\n\tvar modifier := modifier_map_range(1, -1, 100, 0)\n\tmap(_context, _action, input, [modifier])\n\tGUIDE.enable_mapping_context(_context)\n\t\n\t# when i move the joy fully to the left\n\tawait joy_axis(JOY_AXIS_LEFT_X, -1)\n\t# then the action's value is 0\n\tassert_float(_action.value_axis_1d).is_equal_approx(0, 0.01)\n\t\n\t# when i move the joy fully to the right\n\tawait joy_axis(JOY_AXIS_LEFT_X, 1)\n\t# then the action's value is 100\n\tassert_float(_action.value_axis_1d).is_equal_approx(100, 0.01)\n\n\t\nfunc test_modifier_works_with_purely_negative_ranges() -> void:\n\tvar input := input_joy_axis_1d(JOY_AXIS_LEFT_X)\n\tvar modifier := modifier_map_range(-2, -1, -100, -50)\n\tmap(_context, _action, input, [modifier])\n\tGUIDE.enable_mapping_context(_context)\n\t\n\t# when i move the joy fully to the left\n\tawait joy_axis(JOY_AXIS_LEFT_X, -2)\n\t# then the action's value is -100\n\tassert_float(_action.value_axis_1d).is_equal_approx(-100, 0.01)\n\t\n\t# when i move the joy fully to the right\n\tawait joy_axis(JOY_AXIS_LEFT_X, -1)\n\t# then the action's value is -50\n\tassert_float(_action.value_axis_1d).is_equal_approx(-50, 0.01)\n"
  },
  {
    "path": "tests/test_map_range_modifier.gd.uid",
    "content": "uid://cvas8qx8e5m3a\n"
  },
  {
    "path": "tests/test_mouse_axis_1d_input.gd",
    "content": "extends GUIDETestBase\n\n\nvar _context:GUIDEMappingContext\nvar _action:GUIDEAction\n\nfunc _setup() -> void:\n\t_context = mapping_context()\n\t_action = action_1d()\n\tawait mouse_move_to(Vector2(100, 100))\n\t# give it 2 frames so the new mouse position is now treated as the\n\t# origin\n\tawait wait_f(2)\n\t\n\nfunc test_mouse_axis1d_input_x() -> void:\n\tvar input := input_mouse_axis_1d(GUIDEInputMouseAxis1D.GUIDEInputMouseAxis.X)\n\tmap(_context, _action, input)\n\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\t# WHEN\n\t# i move the mouse\n\tawait mouse_move_by(Vector2(10, 0))\n\n\t# THEN\n\t# the action should be triggered\n\tawait watched.assert_triggered()\n\nfunc test_mouse_axis1d_input_y() -> void:\n\tvar input := input_mouse_axis_1d(GUIDEInputMouseAxis1D.GUIDEInputMouseAxis.Y)\n\tmap(_context, _action, input)\n\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\t# WHEN\n\t# i move the mouse\n\tawait mouse_move_by(Vector2(0, 10))\n\n\t# THEN\n\t# the action should be triggered\n\tawait watched.assert_triggered()\n\n\nfunc test_mouse_axis1d_input_ignores_other_axis_x() -> void:\n\tvar input := input_mouse_axis_1d(GUIDEInputMouseAxis1D.GUIDEInputMouseAxis.X)\n\tmap(_context, _action, input)\n\n\tGUIDE.enable_mapping_context(_context)\n\t# wait for any initial events to drop\n\tawait wait_f(5)\n\tvar watched := watch(_action)\n\n\t# WHEN\n\t# i move the mouse\n\tawait mouse_move_by(Vector2(0, 10))\n\n\t# THEN\n\t# the action should not be triggered\n\twatched.assert_not_triggered()\n\t\nfunc test_mouse_axis1d_input_ignores_other_axis_y() -> void:\n\tvar input := input_mouse_axis_1d(GUIDEInputMouseAxis1D.GUIDEInputMouseAxis.Y)\n\tmap(_context, _action, input)\n\tGUIDE.enable_mapping_context(_context)\n\t# wait for any initial events to drop\n\tawait wait_f(5)\n\tvar watched := watch(_action)\n\n\t# WHEN\n\t# i move the mouse\n\tawait mouse_move_by(Vector2(10, 0))\n\n\t# THEN\n\t# the action should not be triggered\n\twatched.assert_not_triggered()\t\n\t\n"
  },
  {
    "path": "tests/test_mouse_axis_1d_input.gd.uid",
    "content": "uid://o2t3r27clp85\n"
  },
  {
    "path": "tests/test_mouse_axis_2d_input.gd",
    "content": "extends GUIDETestBase\n\n\nvar _context:GUIDEMappingContext\nvar _action:GUIDEAction\n\nfunc _setup() -> void:\n\t_context = mapping_context()\n\t_action = action_1d()\n\nfunc test_mouse_axis2d_input() -> void:\n\tvar input := input_mouse_axis_2d()\n\tmap(_context, _action, input)\n\t\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\t# WHEN\n\t# i move the mouse\n\tawait mouse_move_by(Vector2(20, 20))\n\n\t# THEN\n\t# the action should be triggered\n\tawait watched.assert_triggered()\n\n"
  },
  {
    "path": "tests/test_mouse_axis_2d_input.gd.uid",
    "content": "uid://couju8u1vun1\n"
  },
  {
    "path": "tests/test_mouse_button_input.gd",
    "content": "extends GUIDETestBase\n\n\nvar _context:GUIDEMappingContext\nvar _action:GUIDEAction\n\nfunc _setup() -> void:\n\t_context = mapping_context()\n\t_action = action_bool()\n\nfunc test_mouse_button_input() -> void:\n\tvar input := input_mouse_button()\n\tmap(_context, _action, input)\n\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\t# WHEN\n\t# i press the mouse button\n\tawait tap_mouse(MOUSE_BUTTON_LEFT)\n\n\t# THEN\n\t# the action should be triggered\n\tawait watched.assert_triggered()\n\n"
  },
  {
    "path": "tests/test_mouse_button_input.gd.uid",
    "content": "uid://u3tvddvlf01l\n"
  },
  {
    "path": "tests/test_multiple_input_events_per_frame.gd",
    "content": "extends GUIDETestBase\n\n\nvar _context:GUIDEMappingContext\nvar _action:GUIDEAction\n\nfunc _setup() -> void:\n\t_context = mapping_context()\n\t_action = action_bool()\n\n# https://github.com/godotneers/G.U.I.D.E/issues/77\n# Having an actuation and release event in the same frame causes\n# missing trigger of an action.\nfunc test_rapid_key_actuations_are_handled_correctly() -> void:\n\tvar input:GUIDEInput = input_key(KEY_A)\n\tmap(_context, _action, input)\n\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\twatched.assert_not_completed()\n\n\t# WHEN\n\t# i press the key down\n\tkey_down(KEY_A, false)\n\t# and immediately release it within the same frame\n\tawait key_up(KEY_A)\n\n\t# THEN\n\t# the action is triggered\n\tawait watched.assert_triggered()\n\t# and is completed afterwards\n\tawait watched.assert_completed()\n\n\nfunc test_rapid_mouse_actuations_are_handled_correctly() -> void:\n\tvar input:GUIDEInput = input_mouse_button(MOUSE_BUTTON_LEFT)\n\tmap(_context, _action, input)\n\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\twatched.assert_not_completed()\n\n\t# WHEN\n\t# i press the mouse button down\n\tmouse_down(MOUSE_BUTTON_LEFT, false)\n\t# and immediately release it within the same frame\n\tawait mouse_up(MOUSE_BUTTON_LEFT)\n\n\t# THEN\n\t# the action is triggered\n\tawait watched.assert_triggered()\n\t# and is completed afterwards\n\tawait watched.assert_completed()\n\n\nfunc test_rapid_controller_actuations_are_handled_correctly() -> void:\n\tvar input:GUIDEInput = input_joy_button(JOY_BUTTON_A)\n\tmap(_context, _action, input)\n\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\twatched.assert_not_completed()\n\n\t# WHEN\n\t# i press the controller A button down\n\tjoy_button_down(JOY_BUTTON_A, false)\n\t# and immediately release it within the same frame\n\tawait joy_button_up(JOY_BUTTON_A)\n\n\t# THEN\n\t# the action is triggered\n\tawait watched.assert_triggered()\n\t# and is completed afterwards\n\tawait watched.assert_completed()\n"
  },
  {
    "path": "tests/test_multiple_input_events_per_frame.gd.uid",
    "content": "uid://cnf7u2e2aymhb\n"
  },
  {
    "path": "tests/test_node_leaks.gd",
    "content": "extends GUIDETestBase\n\n# Test for https://github.com/godotneers/G.U.I.D.E/issues/13\nfunc test_node_leak() -> void:\n\tvar context := mapping_context()\n\t@warning_ignore(\"shadowed_variable\")\n\tvar action := action_bool()\n\tvar input := input_key(KEY_Q)\n\t\n\tmap(context, action, input)\n\t\n\tGUIDE.enable_mapping_context(context)\n\n\t# test formatting something\t\n\tvar formatter := GUIDEInputFormatter.for_active_contexts()\n\tvar text := await formatter.action_as_richtext_async(action)\n\tassert_str(text).contains(\"[img\")\n\t\n\t# WHEN: i run the cdleanup\n\tvar orphans_before := Performance.get_monitor(Performance.OBJECT_ORPHAN_NODE_COUNT)\n\t\n\tGUIDEInputFormatter.cleanup()\n\t# give the thing one frame to let the cleanup kick in\n\tawait get_tree().process_frame\n\t\n\t# THEN: the cleanup works\n\tvar orphans := Performance.get_monitor(Performance.OBJECT_ORPHAN_NODE_COUNT)\n\tassert_float(orphans).is_less(orphans_before)\n"
  },
  {
    "path": "tests/test_node_leaks.gd.uid",
    "content": "uid://dm51imtdnxs54\n"
  },
  {
    "path": "tests/test_pause_mode.gd",
    "content": "extends GUIDETestBase\n\nvar _context:GUIDEMappingContext\nvar _action:GUIDEAction\nvar _trigger:GUIDETrigger\n\nfunc _setup() -> void:\n\t_context = mapping_context()\n\t_action = action_bool()\n\t_trigger = trigger_down()\n\nfunc _test_pause_mode() -> void:\n\tvar input := input_key(KEY_Q)\n\tmap(_context, _action, input, [], [_trigger])\n\tGUIDE.enable_mapping_context(_context)\n\tvar a := watch(_action)\n\n\n\t# WHEN: the game is paused\n\tget_tree().paused = true\n\n\t# AND: i press the key\n\tawait key_down(KEY_Q)\n\n\t# THEN: the action is still triggered\n\tawait a.assert_triggered(1.0)\n\n\tawait key_up(KEY_Q)\n\n\tget_tree().paused = false\n"
  },
  {
    "path": "tests/test_pause_mode.gd.uid",
    "content": "uid://c40r4yjo8gw48\n"
  },
  {
    "path": "tests/test_released_trigger.gd",
    "content": "## This test verifies the GUIDETriggerReleased class.\nextends GUIDETestBase\n\nvar _context: GUIDEMappingContext\nvar _action: GUIDEAction\nvar _trigger: GUIDETriggerReleased\n\nfunc _setup() -> void:\n\t_context = mapping_context()\n\t_action = action_bool()\n\t_trigger = trigger_released()\n\n\nfunc test_released_trigger_works_for_key_input() -> void:\n\tvar input := input_key(KEY_R)\n\tmap(_context, _action, input, [], [_trigger])\n\tGUIDE.enable_mapping_context(_context)\n\n\tvar watched := watch(_action)\n\n\t# Initial state: nothing\n\twatched.assert_not_triggered()\n\twatched.assert_not_ongoing()\n\n\t# WHEN: I press the key\n\tawait key_down(KEY_R)\n\n\t# THEN: it is ongoing, but not yet triggered\n\tawait watched.assert_started()\n\tawait watched.assert_ongoing()\n\twatched.assert_not_triggered()\n\n\t# WHEN: I release the key\n\tawait key_up(KEY_R)\n\n\t# THEN: it is triggered and then completed\n\tawait watched.assert_triggered()\n\tawait watched.assert_just_triggered()\n\tawait watched.assert_completed()\n\n"
  },
  {
    "path": "tests/test_released_trigger.gd.uid",
    "content": "uid://fnfr4ixfw3dt6\n"
  },
  {
    "path": "tests/test_touch_angle_input.gd",
    "content": "extends GUIDETestBase\n\n\nvar _context:GUIDEMappingContext\nvar _action:GUIDEAction\n\nfunc _setup() -> void:\n\t_context = mapping_context()\n\t_action = action_1d()\n\nfunc test_touch_angle_input() -> void:\n\tvar input := input_touch_angle()\n\tinput.unit = GUIDEInputTouchAngle.AngleUnit.DEGREES\n\tmap(_context, _action, input)\n\t\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\tvar holder:Array[float] = [0.0]\n\t_action.triggered.connect(func() -> void: holder[0] = _action.value_axis_1d)\n\n\t# WHEN\n\t# I rotate my fingers 90 degrees\n\tawait finger_down(0, Vector2(50, 50))\n\tawait finger_down(1, Vector2(100, 50))\n\tawait finger_move(1, Vector2(50, 0))\n\n\t# THEN\n\t# the action is triggered\n\tawait watched.assert_triggered()\n\t\n\t# and the value is 90 degrees\n\tassert_float(holder[0]).is_equal_approx(90, 0.5)"
  },
  {
    "path": "tests/test_touch_angle_input.gd.uid",
    "content": "uid://dxel4i3rdul16\n"
  },
  {
    "path": "tests/test_touch_axis_1d_input.gd",
    "content": "extends GUIDETestBase\n\n\nvar _context:GUIDEMappingContext\nvar _action:GUIDEAction\n\nfunc _setup() -> void:\n\t_context = mapping_context()\n\t_action = action_1d()\n\nfunc test_touch_axis_1d_input() -> void:\n\tvar input := input_touch_axis_1d(GUIDEInputTouchAxis1D.GUIDEInputTouchAxis.X)\n\tmap(_context, _action, input)\n\t\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\t# WHEN\n\t# I move my finger on the screen\n\tawait finger_down(0, Vector2(50, 50))\n\tawait finger_move(0, Vector2(100, 0))\n\n\t# THEN\n\t# the action is triggered\n\tawait watched.assert_triggered()\n"
  },
  {
    "path": "tests/test_touch_axis_1d_input.gd.uid",
    "content": "uid://068v1s5y2e0h\n"
  },
  {
    "path": "tests/test_touch_axis_2d_input.gd",
    "content": "extends GUIDETestBase\n\n\nvar _context:GUIDEMappingContext\nvar _action:GUIDEAction\n\nfunc _setup() -> void:\n\t_context = mapping_context()\n\t_action = action_2d()\n\nfunc test_touch_axis_2d_input() -> void:\n\tvar input := input_touch_axis_2d()\n\tmap(_context, _action, input)\n\t\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\t# WHEN\n\t# I move my finger on the screen\n\tawait finger_down(0, Vector2(50, 50))\n\tawait finger_move(0, Vector2(100, 0))\n\n\t# THEN\n\t# the action is triggered\n\tawait watched.assert_triggered()\n"
  },
  {
    "path": "tests/test_touch_axis_2d_input.gd.uid",
    "content": "uid://dgr7my8w6tgh8\n"
  },
  {
    "path": "tests/test_touch_distance_input.gd",
    "content": "extends GUIDETestBase\n\n\nvar _context:GUIDEMappingContext\nvar _action:GUIDEAction\n\nfunc _setup() -> void:\n\t_context = mapping_context()\n\t_action = action_1d()\n\nfunc test_touch_distance_input_triggers_on_fingers_moving_closer() -> void:\n\tvar input := input_touch_distance()\n\tmap(_context, _action, input)\n\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\t# WHEN\n\t# I move fingers toward each toher\n\tawait finger_down(0, Vector2(0, 0))\n\tawait finger_down(1, Vector2(200, 200))\n\tawait finger_move(1, Vector2(100, 100))\n\n\t# THEN\n\t# the action is triggered\n\tawait watched.assert_triggered()\n\t\nfunc test_touch_distance_input_on_fingers_moving_apart() -> void:\n\tvar input := input_touch_distance()\n\tmap(_context, _action, input)\n\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\t# WHEN\n\t# I move fingers away from each other\n\tawait finger_down(0, Vector2(0, 0))\n\tawait finger_down(1, Vector2(100, 100))\n\tawait finger_move(1, Vector2(200, 200))\n\n\t# THEN\n\t# the action is triggered\n\tawait watched.assert_triggered()\n\t\n\nfunc test_touch_distance_input_doesnt_trigger_on_fingers_standing_still() -> void:\n\tvar input := input_touch_distance()\n\tmap(_context, _action, input)\n\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\t# WHEN\n\t# I keep fingers still\n\tawait finger_down(0, Vector2(0, 0))\n\tawait finger_down(1, Vector2(200, 200))\n\n\t# THEN\n\t# the action is not triggered\n\twatched.assert_not_triggered()\n"
  },
  {
    "path": "tests/test_touch_distance_input.gd.uid",
    "content": "uid://co0csptdw5pvm\n"
  },
  {
    "path": "tests/test_touch_position_input.gd",
    "content": "extends GUIDETestBase\n\n\nvar _context:GUIDEMappingContext\nvar _action:GUIDEAction\n\nfunc _setup() -> void:\n\t_context = mapping_context()\n\t_action = action_2d()\n\nfunc test_touch_position_input() -> void:\n\tvar input := input_touch_position()\n\tmap(_context, _action, input)\n\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\t# WHEN\n\t# I put my finger on the screen\n\tawait finger_down(0, Vector2(50, 50))\n\n\t# THEN\n\t# the action is triggered\n\tawait watched.assert_triggered()\n\t\n\tassert_vector(_action.value_axis_2d).is_equal(Vector2(50, 50))\n\t\nfunc test_touch_position_input_with_multiple_fingers() -> void:\n\tvar input := input_touch_position(2, 3)\n\tmap(_context, _action, input)\n\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\t# WHEN\n\t# I put 3 fingerrs on the screen\n\tawait finger_down(0, Vector2(50, 50))\n\tawait finger_down(1, Vector2(150, 50))\n\tawait finger_down(2, Vector2(300, 50))\n\n\t# THEN\n\t# the action is triggered\n\tawait watched.assert_triggered()\n\t\n\t# and i get the third finger's value\n\tassert_vector(_action.value_axis_2d).is_equal(Vector2(300, 50))\n\t\nfunc test_touch_position_input_with_multiple_fingers_doesnt_trigger_if_not_enough_fingers() -> void:\n\tvar input := input_touch_position(2, 3)\n\tmap(_context, _action, input)\n\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\t# WHEN\n\t# I put 2 fingerrs on the screen\n\tawait finger_down(0, Vector2(50, 50))\n\tawait finger_down(1, Vector2(150, 50))\n\n\t# THEN\n\t# the action is not triggered\n\twatched.assert_not_triggered()\n\t\n\t\n\nfunc test_touch_position_input_with_multiple_fingers_calculates_average() -> void:\n\tvar input := input_touch_position(-1, 3)\n\tmap(_context, _action, input)\n\n\tGUIDE.enable_mapping_context(_context)\n\tvar watched := watch(_action)\n\n\t# WHEN\n\t# I put 3 fingerrs on the screen\n\tawait finger_down(0, Vector2(0, 0))\n\tawait finger_down(1, Vector2(-100, 100))\n\tawait finger_down(2, Vector2(100, 200))\n\n\t# THEN\n\t# the action is triggered\n\tawait watched.assert_triggered()\n\t\n\t# and the value is the average of the three fingers\n\tassert_vector(_action.value_axis_2d).is_equal(Vector2(0, 100))\n"
  },
  {
    "path": "tests/test_touch_position_input.gd.uid",
    "content": "uid://pkhb4g7dix71\n"
  },
  {
    "path": "tests/test_trigger_hair.gd",
    "content": "extends GUIDETestBase\n\nvar _context:GUIDEMappingContext\nvar _action:GUIDEAction\nvar _trigger:GUIDETrigger\n\nvar actuation_threshold:float = 0.5\nvar offset:float = 0.2\n\nfunc _setup() -> void:\n\t_context = mapping_context()\n\t_action = action_1d()\n\t_trigger = trigger_hair(actuation_threshold)\n\n## Tests complete hair trigger behavior: peak/valley tracking and symmetric trigger/release.\nfunc test_trigger_hair_peak_and_valley_tracking() -> void:\n\tvar input := input_joy_axis_1d(JOY_AXIS_TRIGGER_RIGHT)\n\tmap(_context, _action, input, [], [_trigger])\n\tGUIDE.enable_mapping_context(_context)\n\n\tvar watched := watch(_action)\n\n\t# Initial trigger: rise from 0 to threshold\n\tawait joy_axis(JOY_AXIS_TRIGGER_RIGHT, actuation_threshold)\n\tawait watched.assert_triggered()\n\tassert_axis_1d(_action, actuation_threshold)\n\n\t# Peak tracking: rise further while triggered\n\tawait joy_axis(JOY_AXIS_TRIGGER_RIGHT, actuation_threshold + offset)\n\tawait watched.assert_triggered()\n\tassert_axis_1d(_action, actuation_threshold + offset)\n\n\t# Release: drop by threshold from peak (0.7 -> 0.2)\n\tawait joy_axis(JOY_AXIS_TRIGGER_RIGHT, offset)\n\tawait watched.assert_completed()\n\twatched.reset()\n\tassert_axis_1d(_action, offset)\n\n\t# Valley tracking: drop further while not triggered\n\tawait joy_axis(JOY_AXIS_TRIGGER_RIGHT, offset / 2)\n\twatched.assert_not_triggered()\n\tassert_axis_1d(_action, offset / 2)\n\n\t# Re-trigger: rise by threshold from valley (0.1 + 0.5 = 0.6)\n\tawait joy_axis(JOY_AXIS_TRIGGER_RIGHT, offset / 2 + actuation_threshold)\n\tawait watched.assert_triggered()\n\tassert_axis_1d(_action, offset / 2 + actuation_threshold)\n\n\t# Release again: drop by threshold from new peak (0.6 -> 0.1)\n\tawait joy_axis(JOY_AXIS_TRIGGER_RIGHT, offset / 2)\n\tawait watched.assert_completed()\n\tassert_axis_1d(_action, offset / 2)\n"
  },
  {
    "path": "tests/test_trigger_hair.gd.uid",
    "content": "uid://cga2ue2ioffix\n"
  },
  {
    "path": "tests/test_virtual_buttons.gd",
    "content": "extends GUIDETestBase\n\nvar _action:GUIDEAction\nvar _action2:GUIDEAction\n\nfunc _setup() -> void:\n\tvar context := mapping_context()\n\t_action = action_bool(\"Action 1\")\n\tvar input := input_joy_button(JOY_BUTTON_A)\n\tinput.joy_index = -2  # First virtual joy pad\n\tmap(context, _action, input)\n\t\n\t_action2 = action_bool(\"Action 2\")\n\tvar input2 := input_joy_button(JOY_BUTTON_A)\n\tinput2.joy_index = -3  # Second virtual joy pad\n\tmap(context, _action2, input2)\n\t\n\tGUIDE.enable_mapping_context(context)\n\n## Makes a new virtual button with the given input mode at the given position\n## if position is Vector2.INF, it will be placed at a random position\nfunc _make_button(input_mode:GUIDEVirtualButton.InputMode, position:Vector2 = Vector2.INF) -> GUIDEVirtualButton:\n\tvar virtual_button:GUIDEVirtualButton = auto_free(GUIDEVirtualButton.new())\n\tvirtual_button.button_index = JOY_BUTTON_A\n\tvirtual_button.input_mode = input_mode\n\tadd_child(virtual_button)\n\t\n\tif not position.is_finite():\n\t\t# move it to a random position // ensure it's fully on screen\n\t\tvirtual_button.position = Vector2(randf() * 800, randf() * 600) + Vector2(virtual_button.button_radius, virtual_button.button_radius)\n\telse:\n\t\tvirtual_button.position = position\n\t\n\treturn virtual_button\n\nfunc test_virtual_button_can_be_touched() -> void:\n\tvar virtual_button := _make_button(GUIDEVirtualButton.InputMode.TOUCH)\n\tvar watched := watch(_action)\n\t# WHEN i touch the virtual button\n\tawait tap_finger(0, virtual_button.global_position)\n\t# THEN the action is triggered\n\tawait watched.assert_triggered()\n\t\n\t\nfunc test_virtual_button_can_be_clicked() -> void:\n\tvar virtual_button := _make_button(GUIDEVirtualButton.InputMode.MOUSE)\n\tvar watched := watch(_action)\n\n\t# WHEN i click the virtual button\n\tawait tap_mouse_at(MOUSE_BUTTON_LEFT, virtual_button.global_position)\n\t# THEN the action is triggered\n\tawait watched.assert_triggered()\n\n\nfunc test_virtual_button_can_be_clicked_and_touched() -> void:\n\tvar virtual_button := _make_button(GUIDEVirtualButton.InputMode.MOUSE_AND_TOUCH)\n\tvar watched := watch(_action)\n\n\t# WHEN i click the virtual button\n\tawait tap_mouse_at(MOUSE_BUTTON_LEFT, virtual_button.global_position)\n\n\t# THEN the action is triggered\n\tawait watched.assert_triggered()\n\n\t# WHEN i touch the virtual button\n\tawait tap_finger(0, virtual_button.global_position)\n\n\t# THEN the action is triggered\n\tawait watched.assert_triggered()\n\t\t\nfunc test_virtual_button_stops_actuation_when_last_finger_moves_out() -> void:\n\tvar virtual_button := _make_button(GUIDEVirtualButton.InputMode.TOUCH)\n\n\t# WHEN i touch the virtual button with two fingers\n\tawait finger_down(0, virtual_button.global_position)\n\tawait finger_down(1, virtual_button.global_position + Vector2(10,10))\n\t\n\t# THEN the action is triggered\n\tassert_is_triggered(_action)\n\t\n\t# WHEN i move the first finger out of the button area\n\tawait finger_move(0, virtual_button.global_position + Vector2(virtual_button.button_radius + 50, 0))\n\t\n\t# THEN the action is NOT released yet\n\tassert_is_triggered(_action)\n\t\n\t# WHEN i move the second finger out of the button area\n\tawait finger_move(1, virtual_button.global_position + Vector2(virtual_button.button_radius + 50, 0))\n\t\n\t# THEN the action is no longer triggered\n\tassert_is_not_triggered(_action)\n\t\n\tawait finger_up(0)\n\tawait finger_up(1)\n\n\nfunc test_virtual_button_stops_actuation_when_mouse_moves_out() -> void:\n\tvar virtual_button := _make_button(GUIDEVirtualButton.InputMode.MOUSE, Vector2(400,400))\n\n\t# WHEN i move the mouse over the button and press\n\tawait mouse_move_to(virtual_button.global_position)\n\tawait mouse_down(MOUSE_BUTTON_LEFT)\n\t\n\t# THEN the action is triggered\n\tassert_is_triggered(_action)\n\t\n\t# WHEN i move it out, while keeping the button pressed\n\tawait mouse_move_by(Vector2(virtual_button.button_radius + 50, 0))\n\t\n\t# THEN the action is no longer triggered\n\tassert_is_not_triggered(_action)\n\t\n\tawait mouse_up(MOUSE_BUTTON_LEFT)\n\n\nfunc test_multiple_virtual_buttons_on_different_devices_dont_interfere() -> void:\n\tvar virtual_button1 := _make_button(GUIDEVirtualButton.InputMode.TOUCH, Vector2(200,200))\n\tvirtual_button1.virtual_device = 0  # First virtual joy pad\n\t\n\tvar virtual_button2 := _make_button(GUIDEVirtualButton.InputMode.TOUCH, Vector2(600,400))\n\tvirtual_button2.virtual_device = 1  # Second virtual joy pad\n\n\t# WHEN i tap the first virtual button\n\tawait finger_down(0, virtual_button1.global_position)\n\t\n\t# THEN only the first action is triggered\n\tassert_is_triggered(_action)\n\tassert_is_not_triggered(_action2)\n\t\n\t# WHEN i tap the second virtual button\t\n\tawait finger_down(1, virtual_button2.global_position)\n\t\n\t# THEN both actions are triggered\n\tassert_is_triggered(_action)\n\tassert_is_triggered(_action2)\n\t\n\t# WHEN i release the second finger\n\tawait finger_up(1)\n\t\n\t# THEN only the first action still triggered\n\tassert_is_triggered(_action)\n\tassert_is_not_triggered(_action2)\n\t\n\t# WHEN i release the first finger\n\tawait finger_up(0)\n\t# THEN no action is triggered\n\tassert_is_not_triggered(_action)\n\tassert_is_not_triggered(_action2)\n\t\n\t\t\nfunc test_moving_mouse_while_clicking_updates_hit_test() -> void:\n\tvar virtual_button := _make_button(GUIDEVirtualButton.InputMode.MOUSE, Vector2(400,400))\t\t\n\n\t# WHEN i start clicking outside the button area\n\tawait mouse_move_to(Vector2(0, 400))\n\tawait mouse_down(MOUSE_BUTTON_LEFT)\n\t\n\t# THEN the action is not triggered\n\tassert_is_not_triggered(_action)\n\t\n\t# WHEN i move the mouse over the button area while keeping the button pressed\n\tawait mouse_move_to(virtual_button.global_position)\n\t\n\t# THEN the action is triggered\n\tassert_is_triggered(_action)\n\t\n\t# WHEN i move the mouse out of the button area while keeping the button pressed\n\tawait mouse_move_to(Vector2(800, 400))\n\t\n\t# THEN the action is no longer triggered\n\tassert_is_not_triggered(_action)\n\n\tawait mouse_up(MOUSE_BUTTON_LEFT)\n\t\n\t\nfunc test_moving_finger_while_pressing_updates_hit_test() -> void:\n\tvar virtual_button := _make_button(GUIDEVirtualButton.InputMode.TOUCH, Vector2(400,400))\n\n\t# WHEN i start touching outside the button area\n\tawait finger_down(0, Vector2(0, 400))\n\n\t# THEN the action is not triggered\n\tassert_is_not_triggered(_action)\n\n\t# WHEN i move the finger over the button area while keeping it pressed\n\tawait finger_move(0, virtual_button.global_position)\n\n\t# THEN the action is triggered\n\tassert_is_triggered(_action)\n\n\t# WHEN i move the finger out of the button area while keeping it pressed\n\tawait finger_move(0, Vector2(800, 400))\n\n\t# THEN the action is no longer triggered\n\tassert_is_not_triggered(_action)\n\n\tawait finger_up(0)\t\n"
  },
  {
    "path": "tests/test_virtual_buttons.gd.uid",
    "content": "uid://c7pdm42rsqx06\n"
  },
  {
    "path": "tests/test_virtual_cursor.gd",
    "content": "extends GUIDETestBase\n\nvar _vc_action:GUIDEAction\nvar _virtual_cursor:GUIDEModifierVirtualCursor\nvar _mc:GUIDEMappingContext\n\nfunc _setup() -> void:\n\t_mc = mapping_context()\n\t\n\t# Combine D and S into a single action where D moves right and S moves down\n\tvar inputs := action_2d(\"inputs\")\n\tvar left := input_key(KEY_A)\n\tvar right := input_key(KEY_D)\n\tvar up := input_key(KEY_W)\n\tvar down := input_key(KEY_S)\n\tvar swizzle := modifier_input_swizzle()\n\tvar negate := modifier_negate()\n\n\tmap(_mc, inputs, left, [negate])\n\tmap(_mc, inputs, right)\n\tmap(_mc, inputs, up, [negate, swizzle])\n\tmap(_mc, inputs, down, [swizzle])\n\t\n\t\n\t# Now use this action as input for the virtual cursor\n\t_vc_action = action_2d(\"virtual_cursor\")\n\tvar combined_input := input_action(inputs)\n\t_virtual_cursor = modifier_virtual_cursor(Vector2.ZERO)\n\t\n\tmap(_mc, _vc_action, combined_input, [_virtual_cursor])\n\t\n\n\nfunc test_cursor_speed_is_uniform() -> void:\n\tGUIDE.enable_mapping_context(_mc)\n\t\n\t# WHEN\n\tawait tap_keys([KEY_S, KEY_D])\n\t\n\t# THEN\n\tvar value := _vc_action.value_axis_2d\n\tassert_float(value.length()).is_greater(1.0).append_failure_message(\"Cursor didn't move\")\n\tassert_float(value.x).is_equal(value.y).append_failure_message(\"Cursor moved non-uniformly\")\n\t\n\t\nfunc test_cursor_stays_in_frame() -> void:\n\tGUIDE.enable_mapping_context(_mc)\n\t\n\t# WHEN i press left\n\tawait tap_key(KEY_A)\n\t\n\t# THEN i don't go further left\n\tassert_vector(_vc_action.value_axis_2d).is_equal(Vector2.ZERO)\t\n\t\n\t# WHEN i press up\n\tawait tap_key(KEY_W)\n\t\n\t# THEN i don't go further up\n\tassert_vector(_vc_action.value_axis_2d).is_equal(Vector2.ZERO)\t\n\t\n\t# WHEN I hold down and right\n\tawait keys_down([KEY_D, KEY_S])\n\n\t# for a while\n\tawait wait_seconds(2)\n\t\n\tawait keys_up([KEY_D, KEY_S])\n\t\n\tvar size:Vector2 = get_window().size\n\t# then I'm at the window size\n\tassert_vector(_vc_action.value_axis_2d).is_equal(size)\n\t\n\t\n\t\nfunc test_cursor_follows_mouse_position_when_flag_is_set() -> void:\n\t# WHEN: initializing the cursor from mouse position is enabled\n\t_virtual_cursor.initialize_from_mouse_position = true\n\t\n\t# i move the mouse to a random position of the screen\n\tvar position:Vector2 = Vector2(get_window().size) * Vector2(randf(), randf()).round()\n\tawait mouse_move_to(position)\n\t\n\tprint(get_window().get_mouse_position())\n\n\t# and i enable the mapping context\n\tGUIDE.enable_mapping_context(_mc)\n\t\n\t# THEN the cursor is at the same position as the mouse position\n\tassert_vector(_vc_action.value_axis_2d).is_equal_approx(position, Vector2(0.01, 0.01))\n"
  },
  {
    "path": "tests/test_virtual_cursor.gd.uid",
    "content": "uid://bd8728bpoi71y\n"
  },
  {
    "path": "tests/test_virtual_sticks.gd",
    "content": "extends GUIDETestBase\n\nvar _action:GUIDEAction\nvar _action2:GUIDEAction\n\nfunc _setup() -> void:\n\tvar context := mapping_context()\n\n\t# make one action for the left stick\n\t_action = action_2d(\"Action 1\")\n\tvar input := input_joy_axis_2d(JOY_AXIS_LEFT_X, JOY_AXIS_LEFT_Y)\n\tinput.joy_index = -2  # First virtual joy pad\n\tmap(context, _action, input)\n\n\t# make another one for the right stick\n\t_action2 = action_2d(\"Action 2\")\n\tvar input2 := input_joy_axis_2d(JOY_AXIS_RIGHT_X, JOY_AXIS_RIGHT_Y)\n\tinput2.joy_index = -2  # First virtual joy pad\n\tmap(context, _action2, input2)\n\n\tGUIDE.enable_mapping_context(context)\n\n\n## Makes a new virtual stick with the given input mode at the given position\n## if position is Vector2.INF, it will be placed at a random position\nfunc _make_stick(input_mode:GUIDEVirtualStick.InputMode, stick_position:GUIDEVirtualStick.StickPosition, position:Vector2 = Vector2.INF) -> GUIDEVirtualStick:\n\tvar virtual_stick:GUIDEVirtualStick = auto_free(GUIDEVirtualStick.new())\n\tvirtual_stick.stick_position = stick_position\n\tvirtual_stick.input_mode = input_mode\n\tadd_child(virtual_stick)\n\t\n\tif not position.is_finite():\n\t\t# move it to a random position\n\t\tvirtual_stick.position = Vector2(randf() * 800, randf() * 600)\n\telse:\n\t\tvirtual_stick.position = position\n\t\n\treturn virtual_stick\n\n\nfunc test_virtual_stick_can_be_touched() -> void:\n\tvar virtual_stick := _make_stick(GUIDEVirtualStick.InputMode.TOUCH, GUIDEVirtualStick.StickPosition.LEFT, Vector2(400,400))\n\t\n\t# WHEN i touch the virtual stick and move it to the right\n\tawait finger_down(0, virtual_stick.global_position)\n\tawait finger_move(0, virtual_stick.global_position + Vector2(100, 0))\n\t\n\t# THEN the action should be triggered\n\tassert_is_triggered(_action)\n\t\n\t# and its 2D value should be close to 1,0\n\tassert_axis_2d(_action, Vector2(1, 0))\n\t\n\t# WHEN i release the finger\n\tawait finger_up(0)\n\t\n\t# THEN the action should be released\n\tassert_is_not_triggered(_action)\n\n\t# AND its value should be back to 0,0\n\tassert_axis_2d(_action, Vector2.ZERO)\n\n\nfunc test_virtual_stick_can_be_moved_with_the_mouse() -> void:\n\tvar virtual_stick := _make_stick(GUIDEVirtualStick.InputMode.MOUSE, GUIDEVirtualStick.StickPosition.LEFT, Vector2(400,400))\n\t\n\t# WHEN i click the virtual stick and move it to the right\n\tawait mouse_move_to(virtual_stick.global_position)\n\tawait mouse_down(MOUSE_BUTTON_LEFT)\n\tawait mouse_move_to(virtual_stick.global_position + Vector2(100, 0))\n\t\n\t# THEN the action should be triggered\n\tassert_is_triggered(_action)\n\t\n\t# AND its 2D value should be close to 1.0\n\tassert_axis_2d(_action, Vector2(1, 0))\n\t\n\t# WHEN i release the mouse\n\tawait mouse_up(MOUSE_BUTTON_LEFT)\n\t\n\t# THEN the action should be released\n\tassert_is_not_triggered(_action)\n\n\t# AND its value should be back to 0,0\n\tassert_axis_2d(_action, Vector2.ZERO)\n\t\n\t\n\t\nfunc test_multiple_virtual_sticks_can_be_moved_independently_by_touch() -> void:\n\tvar stick1 := _make_stick(GUIDEVirtualStick.InputMode.TOUCH, GUIDEVirtualStick.StickPosition.LEFT, Vector2(400,400))\n\tvar stick2 := _make_stick(GUIDEVirtualStick.InputMode.TOUCH, GUIDEVirtualStick.StickPosition.RIGHT, Vector2(800,400))\n\n\t# WHEN i move the first stick to the left\n\tawait finger_down(0, stick1.global_position)\n\tawait finger_move(0, stick1.global_position + Vector2(-100, 0))\n\t\n\t# THEN the first action value is -1, 0\n\tassert_axis_2d(_action, Vector2(-1, 0))\n\t\n\t# WHEN i now move the second stick to the right with another finger\n\tawait finger_down(1, stick2.global_position)\n\tawait finger_move(1, stick2.global_position + Vector2(100, 0))\n\t\n\t# THEN the second action value is 1, 0\n\tassert_axis_2d(_action2, Vector2(1, 0))\n\t\n\t# AND the first action value is still -1, 0\n\tassert_axis_2d(_action, Vector2(-1, 0))\n\t\n\t# WHEN i now move the first finger to the right and the second to the left\n\tawait finger_move(0, stick1.global_position + Vector2(100, 0))\n\tawait finger_move(1, stick2.global_position + Vector2(-100, 0))\n\n\t# THEN the action values follow the finger movement\n\tassert_axis_2d(_action, Vector2(1, 0))\n\tassert_axis_2d(_action2, Vector2(-1, 0))\n\n\nfunc test_fixed_mode_rejects_outside_actuation_touch_and_mouse() -> void:\n\t# TOUCH: create a fixed-mode touch stick and touch outside the stick radius\n\tvar stick := _make_stick(GUIDEVirtualStick.InputMode.MOUSE_AND_TOUCH, GUIDEVirtualStick.StickPosition.LEFT, Vector2(400,400))\n\tstick.position_mode = GUIDEVirtualStick.PositionMode.FIXED\n\n\t# WHEN touch outside of stick_radius (default 100) \n\tawait finger_down(0, stick.global_position + Vector2(150, 0))\n\t# THEN the action should not be triggered\n\tassert_is_not_triggered(_action)\n\tassert_axis_2d(_action, Vector2.ZERO)\n\tawait finger_up(0)\n\n\t# WHEN click outside of stick_radius (default 100)\n\tawait mouse_move_to(stick.global_position + Vector2(150, 0))\n\tawait mouse_down(MOUSE_BUTTON_LEFT)\n\t# THEN the action should not be triggered\n\tassert_is_not_triggered(_action)\n\tassert_axis_2d(_action, Vector2.ZERO)\n\tawait mouse_up(MOUSE_BUTTON_LEFT)\n\n\nfunc test_relative_mode_sets_start_position_and_recenters_on_release() -> void:\n\t# RELATIVE mode should set the start position to the touch point\n\tvar rel_stick := _make_stick(GUIDEVirtualStick.InputMode.TOUCH, GUIDEVirtualStick.StickPosition.LEFT, Vector2(200,200))\n\trel_stick.position_mode = GUIDEVirtualStick.PositionMode.RELATIVE\n\n\t# WHEN touch at some point\n\tawait finger_down(0, rel_stick.global_position + Vector2(10, 0))\n\t# AND move right by 100\n\tawait finger_move(0, rel_stick.global_position + Vector2(110, 0))\n\t# THEN the action should be triggered with x=1\n\tassert_is_triggered(_action)\n\tassert_axis_2d(_action, Vector2(1, 0))\n\n\t# WHEN i release the finger\n\tawait finger_up(0)\n\t# THEN the action should be released and axis recentred\n\tassert_is_not_triggered(_action)\n\tassert_axis_2d(_action, Vector2.ZERO)\n\n\nfunc test_movement_beyond_max_actuation_radius_clamps_reported_axis() -> void:\n\t# Use relative mode so start position is the touch point and movement is easy to reason about\n\tvar clamp_stick := _make_stick(GUIDEVirtualStick.InputMode.TOUCH, GUIDEVirtualStick.StickPosition.LEFT, Vector2(300,300))\n\tclamp_stick.position_mode = GUIDEVirtualStick.PositionMode.RELATIVE\n\t# sanity: ensure max_actuation_radius is the default (100)\n\tassert_float(clamp_stick.max_actuation_radius).is_greater(0)\n\n\t# WHEN I touch and move far beyond the max actuation radius\n\tawait finger_down(0, clamp_stick.global_position)\n\tawait finger_move(0, clamp_stick.global_position + Vector2(500, 0))\n\t\n\t# THEN the action should be triggered\n\tassert_is_triggered(_action)\n\t# AND the reported axis should be clamped to (1,0)\n\tassert_axis_2d(_action, Vector2(1, 0))\n\tawait finger_up(0)\n\n\nfunc test_only_controlling_finger_can_move_the_stick_and_release_stops_updates() -> void:\n\tvar stick := _make_stick(GUIDEVirtualStick.InputMode.TOUCH, GUIDEVirtualStick.StickPosition.LEFT, Vector2(400,400))\n\tstick.position_mode = GUIDEVirtualStick.PositionMode.FIXED\n\n\t# WHEN finger 0 touches and moves the stick to the right\n\tawait finger_down(0, stick.global_position)\n\tawait finger_move(0, stick.global_position + Vector2(100, 0))\n\t\n\t# THEN the action should be triggered and moved to (1,0)\n\tassert_is_triggered(_action)\n\tassert_axis_2d(_action, Vector2(1, 0))\n\n\t# WHEN a second finger (1) touches and moves the stick to the left\n\tawait finger_down(1, stick.global_position)\n\tawait finger_move(1, stick.global_position + Vector2(-100, 0))\n\t# THEN the action should still be controlled by finger 0 and remain at (1,0)\n\tassert_axis_2d(_action, Vector2(1, 0))\n\n\t# WHEN finger 0 moves the stick to the left\n\tawait finger_move(0, stick.global_position + Vector2(-100, 0))\n\t# THEN the action should follow finger 0 again and move to (-1,0)\n\tassert_axis_2d(_action, Vector2(-1, 0))\n\n\t# WHEN finger 0 releases\n\tawait finger_up(0)\n\t# THEN the action should be released\n\tassert_is_not_triggered(_action)\n\tassert_axis_2d(_action, Vector2.ZERO)\n\n\t# WHEN finger 1 now moves the stick to the right\n\tawait finger_move(1, stick.global_position + Vector2(100, 0))\n\t# THEN the action should not be triggered as finger 1 is not controlling the stick\n\tassert_is_not_triggered(_action)\n\tassert_axis_2d(_action, Vector2.ZERO)\n"
  },
  {
    "path": "tests/test_virtual_sticks.gd.uid",
    "content": "uid://denil64ua4ojc\n"
  }
]