gd: added menu template
@ -6,6 +6,121 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="export_presets.cfg" />
|
<Content Include="export_presets.cfg" />
|
||||||
|
<Content Include="menus\assets\git_logo\Git-Logo-2Color.png" />
|
||||||
|
<Content Include="menus\assets\git_logo\Git-Logo-2Color.png.import" />
|
||||||
|
<Content Include="menus\assets\git_logo\LICENSE.txt" />
|
||||||
|
<Content Include="menus\assets\godot_engine_logo\LICENSE.txt" />
|
||||||
|
<Content Include="menus\assets\godot_engine_logo\logo_vertical_color_dark.png" />
|
||||||
|
<Content Include="menus\assets\godot_engine_logo\logo_vertical_color_dark.png.import" />
|
||||||
|
<Content Include="menus\assets\icon.png" />
|
||||||
|
<Content Include="menus\assets\icon.png.import" />
|
||||||
|
<Content Include="menus\ATTRIBUTION.md" />
|
||||||
|
<Content Include="menus\resources\themes\expedition.tres" />
|
||||||
|
<Content Include="menus\resources\themes\gravity.tres" />
|
||||||
|
<Content Include="menus\resources\themes\grow.tres" />
|
||||||
|
<Content Include="menus\resources\themes\lab.tres" />
|
||||||
|
<Content Include="menus\resources\themes\lore.tres" />
|
||||||
|
<Content Include="menus\resources\themes\steal_this_theme.tres" />
|
||||||
|
<Content Include="menus\scenes\credits\scrollable_credits.gd" />
|
||||||
|
<Content Include="menus\scenes\credits\scrollable_credits.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\credits\scrollable_credits.tscn" />
|
||||||
|
<Content Include="menus\scenes\credits\scrolling_credits.gd" />
|
||||||
|
<Content Include="menus\scenes\credits\scrolling_credits.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\credits\scrolling_credits.tscn" />
|
||||||
|
<Content Include="menus\scenes\end_credits\end_credits.gd" />
|
||||||
|
<Content Include="menus\scenes\end_credits\end_credits.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\end_credits\end_credits.tscn" />
|
||||||
|
<Content Include="menus\scenes\game_scene\configurable_sub_viewport.gd" />
|
||||||
|
<Content Include="menus\scenes\game_scene\configurable_sub_viewport.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\game_scene\game_ui.tscn" />
|
||||||
|
<Content Include="menus\scenes\game_scene\input_display_label.gd" />
|
||||||
|
<Content Include="menus\scenes\game_scene\input_display_label.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\game_scene\levels\level.gd" />
|
||||||
|
<Content Include="menus\scenes\game_scene\levels\level.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\game_scene\levels\level_1.tscn" />
|
||||||
|
<Content Include="menus\scenes\game_scene\levels\level_2.tscn" />
|
||||||
|
<Content Include="menus\scenes\game_scene\levels\level_3.tscn" />
|
||||||
|
<Content Include="menus\scenes\game_scene\tutorials\tutorial_1.tscn" />
|
||||||
|
<Content Include="menus\scenes\game_scene\tutorials\tutorial_2.tscn" />
|
||||||
|
<Content Include="menus\scenes\game_scene\tutorials\tutorial_3.tscn" />
|
||||||
|
<Content Include="menus\scenes\game_scene\tutorial_manager.gd" />
|
||||||
|
<Content Include="menus\scenes\game_scene\tutorial_manager.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\loading_screen\level_loading_screen.tscn" />
|
||||||
|
<Content Include="menus\scenes\loading_screen\loading_screen.gd" />
|
||||||
|
<Content Include="menus\scenes\loading_screen\loading_screen.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\loading_screen\loading_screen.tscn" />
|
||||||
|
<Content Include="menus\scenes\loading_screen\loading_screen_with_shader_caching.gd" />
|
||||||
|
<Content Include="menus\scenes\loading_screen\loading_screen_with_shader_caching.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\loading_screen\loading_screen_with_shader_caching.tscn" />
|
||||||
|
<Content Include="menus\scenes\menus\level_select_menu\level_select_menu.gd" />
|
||||||
|
<Content Include="menus\scenes\menus\level_select_menu\level_select_menu.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\menus\level_select_menu\level_select_menu.tscn" />
|
||||||
|
<Content Include="menus\scenes\menus\main_menu\main_menu.gd" />
|
||||||
|
<Content Include="menus\scenes\menus\main_menu\main_menu.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\menus\main_menu\main_menu.tscn" />
|
||||||
|
<Content Include="menus\scenes\menus\main_menu\main_menu_with_animations.gd" />
|
||||||
|
<Content Include="menus\scenes\menus\main_menu\main_menu_with_animations.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\menus\main_menu\main_menu_with_animations.tscn" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\audio\audio_input_option_control.gd" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\audio\audio_input_option_control.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\audio\audio_input_option_control.tscn" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\audio\audio_options_menu.gd" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\audio\audio_options_menu.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\audio\audio_options_menu.tscn" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\game\game_options_menu.gd" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\game\game_options_menu.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\game\game_options_menu.tscn" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\game\reset_game_control\reset_game_control.gd" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\game\reset_game_control\reset_game_control.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\game\reset_game_control\reset_game_control.tscn" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\input\input_extras_menu.tscn" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\input\input_options_menu.gd" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\input\input_options_menu.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\input\input_options_menu.tscn" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\input\input_options_menu_with_mouse_sensitivity.tscn" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\master_options_menu.gd" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\master_options_menu.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\master_options_menu.tscn" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\master_options_menu_with_tabs.tscn" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\mini_options_menu.gd" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\mini_options_menu.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\mini_options_menu.tscn" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\mini_options_menu_with_reset.gd" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\mini_options_menu_with_reset.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\mini_options_menu_with_reset.tscn" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\video\video_options_menu.gd" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\video\video_options_menu.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\video\video_options_menu.tscn" />
|
||||||
|
<Content Include="menus\scenes\menus\options_menu\video\video_options_menu_with_extras.tscn" />
|
||||||
|
<Content Include="menus\scenes\opening\opening.gd" />
|
||||||
|
<Content Include="menus\scenes\opening\opening.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\opening\opening.tscn" />
|
||||||
|
<Content Include="menus\scenes\opening\opening_with_logo.tscn" />
|
||||||
|
<Content Include="menus\scenes\overlaid_menus\game_won_menu.gd" />
|
||||||
|
<Content Include="menus\scenes\overlaid_menus\game_won_menu.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\overlaid_menus\game_won_menu.tscn" />
|
||||||
|
<Content Include="menus\scenes\overlaid_menus\level_lost_menu.gd" />
|
||||||
|
<Content Include="menus\scenes\overlaid_menus\level_lost_menu.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\overlaid_menus\level_lost_menu.tscn" />
|
||||||
|
<Content Include="menus\scenes\overlaid_menus\level_won_menu.gd" />
|
||||||
|
<Content Include="menus\scenes\overlaid_menus\level_won_menu.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\overlaid_menus\level_won_menu.tscn" />
|
||||||
|
<Content Include="menus\scenes\overlaid_menus\mini_options_overlaid_menu.tscn" />
|
||||||
|
<Content Include="menus\scenes\overlaid_menus\overlaid_menu.gd" />
|
||||||
|
<Content Include="menus\scenes\overlaid_menus\overlaid_menu.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\overlaid_menus\overlaid_menu.tscn" />
|
||||||
|
<Content Include="menus\scenes\overlaid_menus\overlaid_menu_container.gd" />
|
||||||
|
<Content Include="menus\scenes\overlaid_menus\overlaid_menu_container.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\overlaid_menus\overlaid_menu_container.tscn" />
|
||||||
|
<Content Include="menus\scenes\overlaid_menus\pause_menu.gd" />
|
||||||
|
<Content Include="menus\scenes\overlaid_menus\pause_menu.gd.uid" />
|
||||||
|
<Content Include="menus\scenes\overlaid_menus\pause_menu.tscn" />
|
||||||
|
<Content Include="menus\scripts\game_state.gd" />
|
||||||
|
<Content Include="menus\scripts\game_state.gd.uid" />
|
||||||
|
<Content Include="menus\scripts\level_list_and_state_manager.gd" />
|
||||||
|
<Content Include="menus\scripts\level_list_and_state_manager.gd.uid" />
|
||||||
|
<Content Include="menus\scripts\level_state.gd" />
|
||||||
|
<Content Include="menus\scripts\level_state.gd.uid" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="addons\" />
|
<Folder Include="addons\" />
|
||||||
|
38
addons/maaacks_game_template/ATTRIBUTION.md
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# Attribution
|
||||||
|
## Collaborators
|
||||||
|
|
||||||
|
### Godot Game Template
|
||||||
|

|
||||||
|
Author: [Marek Belski and contributors](https://github.com/Maaack/Godot-Game-Template/graphs/contributors)
|
||||||
|
Source: [github: Godot-Game-Template](https://github.com/Maaack/Godot-Game-Template)
|
||||||
|
License: [MIT License](LICENSE.txt)
|
||||||
|
|
||||||
|
## Sourced
|
||||||
|
#### Godot Engine Logo
|
||||||
|
Author: Andrea Calabró
|
||||||
|
Source: [godotengine.org : press](https://godotengine.org/press/)
|
||||||
|
License: [CC BY 4.0 International](https://github.com/godotengine/godot/blob/master/LOGO_LICENSE.txt)
|
||||||
|
|
||||||
|
#### Git Logo
|
||||||
|
Author: [Jason Long](https://bsky.app/profile/jasonlong.me)
|
||||||
|
Source: [git-scm.com : logos](https://git-scm.com/downloads/logos)
|
||||||
|
License: [CC BY 3.0](https://creativecommons.org/licenses/by/3.0/)
|
||||||
|
|
||||||
|
## Tools
|
||||||
|
#### Godot
|
||||||
|

|
||||||
|
Author: [Juan Linietsky, Ariel Manzur, and contributors](https://godotengine.org/contact)
|
||||||
|
Source: [godotengine.org](https://godotengine.org/)
|
||||||
|
License: [MIT License](https://github.com/godotengine/godot/blob/master/LICENSE.txt)
|
||||||
|
|
||||||
|
#### Visual Studio Code
|
||||||
|
Author: [Microsoft](https://opensource.microsoft.com/)
|
||||||
|
Source: [github: vscode](https://github.com/microsoft/vscode)
|
||||||
|
License: [MIT License](https://github.com/microsoft/vscode/blob/main/LICENSE.txt)
|
||||||
|
|
||||||
|
#### Git
|
||||||
|

|
||||||
|
Author: [Linus Torvalds](https://github.com/torvalds)
|
||||||
|
Source: [git-scm.com](https://git-scm.com/downloads)
|
||||||
|
License: [GNU General Public License version 2](https://opensource.org/licenses/GPL-2.0)
|
||||||
|
|
19
addons/maaacks_game_template/LICENSE.txt
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2022-present Marek Belski.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
173
addons/maaacks_game_template/README.md
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
# Godot Game Template
|
||||||
|
For Godot 4.4 (4.2+ compatible)
|
||||||
|
|
||||||
|
This template has a main menu, options menus, pause menu, credits, scene loader, extra tools, and an example game scene.
|
||||||
|
|
||||||
|
[Example on itch.io](https://maaack.itch.io/godot-game-template)
|
||||||
|
|
||||||
|
[Featured Games](#featured-games)
|
||||||
|
|
||||||
|
#### Videos
|
||||||
|
|
||||||
|
[](https://youtu.be/U9CB3vKINVw)
|
||||||
|
[More Videos](/addons/maaacks_game_template/docs/Videos.md)
|
||||||
|
|
||||||
|
#### Screenshots
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
[More Screenshots](/addons/maaacks_game_template/docs/Screenshots.md)
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
|
||||||
|
Setup menus and accessibility features in about 15 minutes.
|
||||||
|
|
||||||
|
The template can be the start of a new project, or plug into an existing one. It is game agnostic (2D or 3D) and can work with multiple target resolutions, up to 4k and down to 640x360. It's meant to cover the needs for a typical game jam, while remaining scalable and extensible enough to support commercial games.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
### Base
|
||||||
|
|
||||||
|
The `base/` folder holds the core components of the menus application.
|
||||||
|
|
||||||
|
- Main Menu
|
||||||
|
- Options Menus
|
||||||
|
- Pause Menu
|
||||||
|
- Credits
|
||||||
|
- Loading Screen
|
||||||
|
- Opening Scene
|
||||||
|
- Persistent Settings
|
||||||
|
- Simple Config Interface
|
||||||
|
- Extensible Overlay Menus
|
||||||
|
- Keyboard/Mouse Support
|
||||||
|
- Gamepad Support
|
||||||
|
- UI Sound Controller
|
||||||
|
- Background Music Controller
|
||||||
|
|
||||||
|
### Extras
|
||||||
|
|
||||||
|
The `extras/` folder holds components that extend the core application.
|
||||||
|
|
||||||
|
- Win & Lose Menus
|
||||||
|
- Level Loaders
|
||||||
|
- Level Progress Manager
|
||||||
|
- Logging Scripts
|
||||||
|
- Script for Releasing on [itch.io](https://itch.io/) with [butler](https://itch.io/docs/butler/)
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
The `examples/` folder contains an example project using inherited scenes from the `base/` and `extras/`.
|
||||||
|
|
||||||
|
- Example Game Scene
|
||||||
|
- Base Level Class
|
||||||
|
- Example Levels
|
||||||
|
- End Credits
|
||||||
|
- Additional Inherited Scenes:
|
||||||
|
- Game Options Menu w/ Reset button
|
||||||
|
- Master Options Menu w/ Game Options tab
|
||||||
|
- Main Menu w/ Animations
|
||||||
|
- Opening w/ Godot Logo
|
||||||
|
- Level Loading Screen
|
||||||
|
- Loading Screen w/ Shader Pre-caching
|
||||||
|
|
||||||
|
### Minimal
|
||||||
|
|
||||||
|
Users that want a minimal set of features can try [Maaack's Menus Template](https://github.com/Maaack/Godot-Menus-Template) or other options from the [plugin suite](/addons/maaacks_game_template/docs/PluginSuite.md).
|
||||||
|
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Godot Asset Library
|
||||||
|
This package is available as both a template and a plugin, meaning it can be used to start a new project, or added to an existing project.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
When starting a new project:
|
||||||
|
|
||||||
|
1. Go to the `Asset Library Projects` tab.
|
||||||
|
2. Search for "Maaack's Game Template".
|
||||||
|
3. Click on the result to open the template details.
|
||||||
|
4. Click to Download.
|
||||||
|
5. Give the project a new name and destination.
|
||||||
|
6. Click to Install & Edit.
|
||||||
|
7. Continue with the [New Project Instructions](/addons/maaacks_game_template/docs/NewProject.md)
|
||||||
|
|
||||||
|
When editing an existing project:
|
||||||
|
|
||||||
|
1. Go to the `AssetLib` tab.
|
||||||
|
2. Search for "Maaack's Game Template Plugin".
|
||||||
|
3. Click on the result to open the plugin details.
|
||||||
|
4. Click to Download.
|
||||||
|
5. Check that contents are getting installed to `addons/` and there are no conflicts.
|
||||||
|
6. Click to Install.
|
||||||
|
7. Reload the project (you may see errors before you do this).
|
||||||
|
8. Enable the plugin from the Project Settings > Plugins tab.
|
||||||
|
If it's enabled for the first time,
|
||||||
|
1. A dialogue window will appear asking to copy the example scenes out of `addons/`.
|
||||||
|
2. Another dialogue window will ask to update the project's main scene.
|
||||||
|
9. Continue with the [Existing Project Instructions](/addons/maaacks_game_template/docs/ExistingProject.md)
|
||||||
|
|
||||||
|
|
||||||
|
### GitHub
|
||||||
|
|
||||||
|
|
||||||
|
1. Download the latest release version from [GitHub](https://github.com/Maaack/Godot-Game-Template/releases/latest).
|
||||||
|
2. Extract the contents of the archive.
|
||||||
|
3. Move the `addons/maaacks_game_template` folder into your project's `addons/` folder.
|
||||||
|
4. Open/Reload the project.
|
||||||
|
5. Enable the plugin from the Project Settings > Plugins tab.
|
||||||
|
If it's enabled for the first time,
|
||||||
|
1. A dialogue window will appear asking to copy the example scenes out of `addons/`.
|
||||||
|
2. Another dialogue window will ask to update the project's main scene.
|
||||||
|
6. Continue with the [Existing Project Instructions](/addons/maaacks_game_template/docs/ExistingProject.md)
|
||||||
|
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### New Project
|
||||||
|
These instructions assume starting with the entire contents of the project folder. This will be the case when cloning the repo, or starting from the *template* version in the Godot Asset Library.
|
||||||
|
|
||||||
|
|
||||||
|
[New Project Instructions](/addons/maaacks_game_template/docs/NewProject.md)
|
||||||
|
|
||||||
|
### Existing Project
|
||||||
|
|
||||||
|
These instructions assume starting with just the contents of `addons/`. This will be the case when installing the *plugin* version in the Godot Asset Library.
|
||||||
|
|
||||||
|
[Existing Project Instructions](/addons/maaacks_game_template/docs/ExistingProject.md)
|
||||||
|
|
||||||
|
### More Documentation
|
||||||
|
|
||||||
|
[Main Menu Setup](/addons/maaacks_game_template/docs/MainMenuSetup.md)
|
||||||
|
[Game Scene Setup](/addons/maaacks_game_template/docs/GameSceneSetup.md)
|
||||||
|
[Input Icon Mapping](/addons/maaacks_game_template/docs/InputIconMapping.md)
|
||||||
|
[Joypad Inputs](/addons/maaacks_game_template/docs/JoypadInputs.md)
|
||||||
|
[Game Saving](/addons/maaacks_game_template/docs/GameSaving.md)
|
||||||
|
[How Parts Work](/addons/maaacks_game_template/docs/HowPartsWork.md)
|
||||||
|
[Uploading to itch.io](/addons/maaacks_game_template/docs/UploadingToItchIo.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Featured Games
|
||||||
|
|
||||||
|
| Spud Customs | Rent Seek Kill | A Darkness Like Gravity |
|
||||||
|
| :-------:| :-------: | :-------: |
|
||||||
|
 |  |  |
|
||||||
|
[Find on Steam](https://store.steampowered.com/app/3291880/Spud_Customs/) | [Play on itch.io](https://xandruher.itch.io/rent-seek-kill) | [Play on itch.io](https://maaack.itch.io/a-darkness-like-gravity) |
|
||||||
|
|
||||||
|
|
||||||
|
[All Shared Games](/addons/maaacks_game_template/docs/GamesMade.md)
|
||||||
|
|
||||||
|
|
||||||
|
## Community
|
||||||
|
|
||||||
|
Join the [Discord server](https://discord.gg/AyZrJh5AMp ) and share your work with others. It's also a space for getting or giving feedback, and asking for help.
|
||||||
|
|
||||||
|
|
||||||
|
## Links
|
||||||
|
[Attribution](/addons/maaacks_game_template/ATTRIBUTION.md)
|
||||||
|
[License](/addons/maaacks_game_template/LICENSE.txt)
|
||||||
|
[Godot Asset Library - Template](https://godotengine.org/asset-library/asset/2703)
|
||||||
|
[Godot Asset Library - Plugin](https://godotengine.org/asset-library/asset/2709)
|
BIN
addons/maaacks_game_template/assets/git_logo/Git-Logo-2Color.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://rpqoffvo4a4q"
|
||||||
|
path="res://.godot/imported/Git-Logo-2Color.png-ccd120c6c67dfbab1898730c2a1a23e5.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/maaacks_game_template/assets/git_logo/Git-Logo-2Color.png"
|
||||||
|
dest_files=["res://.godot/imported/Git-Logo-2Color.png-ccd120c6c67dfbab1898730c2a1a23e5.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
6
addons/maaacks_game_template/assets/git_logo/LICENSE.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
Git Logo
|
||||||
|
Copyright (c) Jason Long
|
||||||
|
|
||||||
|
This work is licensed under the Creative Commons Attribution 3.0 Unported
|
||||||
|
license (CC BY 3.0): https://creativecommons.org/licenses/by/3.0/
|
||||||
|
|
@ -0,0 +1,5 @@
|
|||||||
|
Godot Engine Logo
|
||||||
|
Copyright (c) 2017 Andrea Calabró
|
||||||
|
|
||||||
|
This work is licensed under the Creative Commons Attribution 4.0 International
|
||||||
|
license (CC BY 4.0 International): https://creativecommons.org/licenses/by/4.0/
|
After Width: | Height: | Size: 45 KiB |
@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cxodj5plfb52k"
|
||||||
|
path="res://.godot/imported/logo_vertical_color_dark.png-914a689b7551193a70a010921088ebb7.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/maaacks_game_template/assets/godot_engine_logo/logo_vertical_color_dark.png"
|
||||||
|
dest_files=["res://.godot/imported/logo_vertical_color_dark.png-914a689b7551193a70a010921088ebb7.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
BIN
addons/maaacks_game_template/assets/icon.png
Normal file
After Width: | Height: | Size: 60 KiB |
34
addons/maaacks_game_template/assets/icon.png.import
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cgdb2p0ctknhg"
|
||||||
|
path="res://.godot/imported/icon.png-a0fb24a97f6b05ebd95f87936ff4bc85.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/maaacks_game_template/assets/icon.png"
|
||||||
|
dest_files=["res://.godot/imported/icon.png-a0fb24a97f6b05ebd95f87936ff4bc85.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
28
addons/maaacks_game_template/assets/input-icons/License.txt
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
|
||||||
|
Input Prompts (1.1b)
|
||||||
|
|
||||||
|
Created/distributed by Kenney (www.kenney.nl)
|
||||||
|
Creation date: 26-06-2024
|
||||||
|
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
License: (Creative Commons Zero, CC0)
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
|
||||||
|
You can use this content for personal, educational, and commercial purposes.
|
||||||
|
|
||||||
|
Support by crediting 'Kenney' or 'www.kenney.nl' (this is not a requirement)
|
||||||
|
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
• Website : www.kenney.nl
|
||||||
|
• Donate : www.kenney.nl/donate
|
||||||
|
|
||||||
|
• Patreon : patreon.com/kenney
|
||||||
|
|
||||||
|
Follow on social media for updates:
|
||||||
|
|
||||||
|
• Twitter: twitter.com/KenneyNL
|
||||||
|
• Instagram: instagram.com/kenney_nl
|
||||||
|
• Mastodon: mastodon.gamedev.place/@kenney
|
After Width: | Height: | Size: 982 B |
@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://bt1yqttw3d5xn"
|
||||||
|
path="res://.godot/imported/icons-filled-colored-2x.png-14a5dbb04fef712e7a1f7d34f81f0511.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/maaacks_game_template/assets/input-icons/icons-filled-colored-2x.png"
|
||||||
|
dest_files=["res://.godot/imported/icons-filled-colored-2x.png-14a5dbb04fef712e7a1f7d34f81f0511.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
@ -0,0 +1,6 @@
|
|||||||
|
<svg width="64" height="64" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<defs/>
|
||||||
|
<g>
|
||||||
|
<path stroke="none" fill="#7DB700" d="M56 32 Q56 42 48.95 48.95 42 56 32 56 22.05 56 15 48.95 8 42 8 32 8 22.05 15 15 22.05 8 32 8 42 8 48.95 15 56 22.05 56 32 M38 42 L42 42 34 22 30 22 22 42 26 42 27.6 38 36.4 38 38 42 M32 27 L34.8 34 29.2 34 32 27"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 393 B |
@ -0,0 +1,37 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://ix1d2e62f233"
|
||||||
|
path="res://.godot/imported/icons-filled-colored-vector.svg-c7a49006540770527e69f02661f41e5d.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/maaacks_game_template/assets/input-icons/icons-filled-colored-vector.svg"
|
||||||
|
dest_files=["res://.godot/imported/icons-filled-colored-vector.svg-c7a49006540770527e69f02661f41e5d.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
After Width: | Height: | Size: 539 B |
@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cmni5hv40bfaa"
|
||||||
|
path="res://.godot/imported/icons-filled-colored.png-b51ce8c74ea37d4ce19368644717d850.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/maaacks_game_template/assets/input-icons/icons-filled-colored.png"
|
||||||
|
dest_files=["res://.godot/imported/icons-filled-colored.png-b51ce8c74ea37d4ce19368644717d850.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
After Width: | Height: | Size: 982 B |
@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://bit8o3p506th6"
|
||||||
|
path="res://.godot/imported/icons-filled-white-2x.png-5c033b4f193bd04be0bd84ca3aeed43e.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/maaacks_game_template/assets/input-icons/icons-filled-white-2x.png"
|
||||||
|
dest_files=["res://.godot/imported/icons-filled-white-2x.png-5c033b4f193bd04be0bd84ca3aeed43e.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
@ -0,0 +1,6 @@
|
|||||||
|
<svg width="64" height="64" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<defs/>
|
||||||
|
<g>
|
||||||
|
<path stroke="none" fill="#FFFFFF" d="M56 32 Q56 42 48.95 48.95 42 56 32 56 22.05 56 15 48.95 8 42 8 32 8 22.05 15 15 22.05 8 32 8 42 8 48.95 15 56 22.05 56 32 M38 42 L42 42 34 22 30 22 22 42 26 42 27.6 38 36.4 38 38 42 M32 27 L34.8 34 29.2 34 32 27"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 393 B |
@ -0,0 +1,37 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://c37gofthe2bh3"
|
||||||
|
path="res://.godot/imported/icons-filled-white-vector.svg-fb1a35d16d7d3ee4e3b0699c09f3649a.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/maaacks_game_template/assets/input-icons/icons-filled-white-vector.svg"
|
||||||
|
dest_files=["res://.godot/imported/icons-filled-white-vector.svg-fb1a35d16d7d3ee4e3b0699c09f3649a.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
After Width: | Height: | Size: 539 B |
@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://deskx061vlcgx"
|
||||||
|
path="res://.godot/imported/icons-filled-white.png-f0994450aea86cd81dcc11ae094a178f.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/maaacks_game_template/assets/input-icons/icons-filled-white.png"
|
||||||
|
dest_files=["res://.godot/imported/icons-filled-white.png-f0994450aea86cd81dcc11ae094a178f.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cqb86gp1gh3y8"
|
||||||
|
path="res://.godot/imported/icons-outlined-colored-2x.png-5a43a028a51c5c4295143c766d9574a0.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/maaacks_game_template/assets/input-icons/icons-outlined-colored-2x.png"
|
||||||
|
dest_files=["res://.godot/imported/icons-outlined-colored-2x.png-5a43a028a51c5c4295143c766d9574a0.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
@ -0,0 +1,6 @@
|
|||||||
|
<svg width="64" height="64" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<defs/>
|
||||||
|
<g>
|
||||||
|
<path stroke="none" fill="#7DB700" d="M56 32 Q56 42 48.95 48.95 42 56 32 56 22.05 56 15 48.95 8 42 8 32 8 22.05 15 15 22.05 8 32 8 42 8 48.95 15 56 22.05 56 32 M32 27 L29.2 34 34.8 34 32 27 M38 42 L36.4 38 27.6 38 26 42 22 42 30 22 34 22 42 42 38 42 M46.85 17.15 Q40.75 11 32 11 23.3 11 17.15 17.15 11 23.3 11 32 11 40.75 17.15 46.85 23.3 53 32 53 40.75 53 46.85 46.85 53 40.75 53 32 53 23.3 46.85 17.15"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 547 B |
@ -0,0 +1,37 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://bsgf78aysgdnd"
|
||||||
|
path="res://.godot/imported/icons-outlined-colored-vector.svg-c32ed4ee32b32291e81571a12a36394d.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/maaacks_game_template/assets/input-icons/icons-outlined-colored-vector.svg"
|
||||||
|
dest_files=["res://.godot/imported/icons-outlined-colored-vector.svg-c32ed4ee32b32291e81571a12a36394d.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
After Width: | Height: | Size: 673 B |
@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://bohem6w6kcl3x"
|
||||||
|
path="res://.godot/imported/icons-outlined-colored.png-db3d206f9675395a32cb5ad98e6b9065.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/maaacks_game_template/assets/input-icons/icons-outlined-colored.png"
|
||||||
|
dest_files=["res://.godot/imported/icons-outlined-colored.png-db3d206f9675395a32cb5ad98e6b9065.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://d3bsc6o2ae88q"
|
||||||
|
path="res://.godot/imported/icons-outlined-white-2x.png-1e7b9db0c429e31d1923667585542e8c.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/maaacks_game_template/assets/input-icons/icons-outlined-white-2x.png"
|
||||||
|
dest_files=["res://.godot/imported/icons-outlined-white-2x.png-1e7b9db0c429e31d1923667585542e8c.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
@ -0,0 +1,6 @@
|
|||||||
|
<svg width="64" height="64" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<defs/>
|
||||||
|
<g>
|
||||||
|
<path stroke="none" fill="#FFFFFF" d="M56 32 Q56 42 48.95 48.95 42 56 32 56 22.05 56 15 48.95 8 42 8 32 8 22.05 15 15 22.05 8 32 8 42 8 48.95 15 56 22.05 56 32 M32 27 L29.2 34 34.8 34 32 27 M38 42 L36.4 38 27.6 38 26 42 22 42 30 22 34 22 42 42 38 42 M46.85 17.15 Q40.75 11 32 11 23.3 11 17.15 17.15 11 23.3 11 32 11 40.75 17.15 46.85 23.3 53 32 53 40.75 53 46.85 46.85 53 40.75 53 32 53 23.3 46.85 17.15"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 547 B |
@ -0,0 +1,37 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://c1lpc33fpmd4p"
|
||||||
|
path="res://.godot/imported/icons-outlined-white-vector.svg-13bd95bd8aface9a8bed6895685dd4ef.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/maaacks_game_template/assets/input-icons/icons-outlined-white-vector.svg"
|
||||||
|
dest_files=["res://.godot/imported/icons-outlined-white-vector.svg-13bd95bd8aface9a8bed6895685dd4ef.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
After Width: | Height: | Size: 673 B |
@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://bq211jkfnm7k7"
|
||||||
|
path="res://.godot/imported/icons-outlined-white.png-c34cd64ff1b09fbf25cb6339951f61dc.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/maaacks_game_template/assets/input-icons/icons-outlined-white.png"
|
||||||
|
dest_files=["res://.godot/imported/icons-outlined-white.png-c34cd64ff1b09fbf25cb6339951f61dc.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
@ -0,0 +1 @@
|
|||||||
|
Remapping input icons by Marek Belski is marked with CC0 1.0. To view a copy of this license, visit https://creativecommons.org/publicdomain/zero/1.0/
|
After Width: | Height: | Size: 357 B |
@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://c1eqf1cse1hch"
|
||||||
|
path="res://.godot/imported/addition_symbol.png-e8a7f3ce4d91474fb1dc85f298d0b607.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/maaacks_game_template/base/assets/remapping_input_icons/addition_symbol.png"
|
||||||
|
dest_files=["res://.godot/imported/addition_symbol.png-e8a7f3ce4d91474fb1dc85f298d0b607.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
After Width: | Height: | Size: 327 B |
@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://bteq3ica74h30"
|
||||||
|
path="res://.godot/imported/subtraction_symbol.png-88291598586ab54d7f002593f7569b3e.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/maaacks_game_template/base/assets/remapping_input_icons/subtraction_symbol.png"
|
||||||
|
dest_files=["res://.godot/imported/subtraction_symbol.png-88291598586ab54d7f002593f7569b3e.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
@ -0,0 +1,5 @@
|
|||||||
|
extends Node
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
GlobalState.open()
|
||||||
|
AppSettings.set_from_config_and_window(get_window())
|
@ -0,0 +1 @@
|
|||||||
|
uid://cno5ujal5t3kf
|
@ -0,0 +1,6 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://cjke6crjg14a0"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://cno5ujal5t3kf" path="res://addons/maaacks_game_template/base/scenes/autoloads/app_config.gd" id="1_o0k5w"]
|
||||||
|
|
||||||
|
[node name="AppConfig" type="Node"]
|
||||||
|
script = ExtResource("1_o0k5w")
|
@ -0,0 +1,7 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://r5t485lr3p7t"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://ctrh4qyxqncss" path="res://addons/maaacks_game_template/base/scripts/music_controller.gd" id="1_wbudo"]
|
||||||
|
|
||||||
|
[node name="ProjectMusicController" type="Node"]
|
||||||
|
process_mode = 3
|
||||||
|
script = ExtResource("1_wbudo")
|
@ -0,0 +1,6 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://cc37235kj4384"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://b5oej1q4h7jvh" path="res://addons/maaacks_game_template/base/scripts/ui_sound_controller.gd" id="1_dmagn"]
|
||||||
|
|
||||||
|
[node name="ProjectUISoundController" type="Node"]
|
||||||
|
script = ExtResource("1_dmagn")
|
@ -0,0 +1,122 @@
|
|||||||
|
class_name SceneLoaderClass
|
||||||
|
extends Node
|
||||||
|
## Autoload class for loading scenes with an optional loading screen.
|
||||||
|
|
||||||
|
signal scene_loaded
|
||||||
|
|
||||||
|
@export_file("*.tscn") var loading_screen_path : String : set = set_loading_screen
|
||||||
|
|
||||||
|
@export_group("Debug")
|
||||||
|
@export var debug_enabled : bool = false
|
||||||
|
@export var debug_lock_status : ResourceLoader.ThreadLoadStatus
|
||||||
|
@export_range(0, 1) var debug_lock_progress : float = 0.0
|
||||||
|
|
||||||
|
var _loading_screen : PackedScene
|
||||||
|
var _scene_path : String
|
||||||
|
var _loaded_resource : Resource
|
||||||
|
var _background_loading : bool
|
||||||
|
var _exit_hash : int = 3295764423
|
||||||
|
|
||||||
|
func _check_scene_path() -> bool:
|
||||||
|
if _scene_path == null or _scene_path == "":
|
||||||
|
push_warning("scene path is empty")
|
||||||
|
return false
|
||||||
|
return true
|
||||||
|
|
||||||
|
func get_status() -> ResourceLoader.ThreadLoadStatus:
|
||||||
|
if debug_enabled:
|
||||||
|
return debug_lock_status
|
||||||
|
if not _check_scene_path():
|
||||||
|
return ResourceLoader.THREAD_LOAD_INVALID_RESOURCE
|
||||||
|
return ResourceLoader.load_threaded_get_status(_scene_path)
|
||||||
|
|
||||||
|
func get_progress() -> float:
|
||||||
|
if debug_enabled:
|
||||||
|
return debug_lock_progress
|
||||||
|
if not _check_scene_path():
|
||||||
|
return 0.0
|
||||||
|
var progress_array : Array = []
|
||||||
|
ResourceLoader.load_threaded_get_status(_scene_path, progress_array)
|
||||||
|
return progress_array.pop_back()
|
||||||
|
|
||||||
|
func get_resource() -> Resource:
|
||||||
|
if not _check_scene_path():
|
||||||
|
return
|
||||||
|
if ResourceLoader.has_cached(_scene_path):
|
||||||
|
_loaded_resource = ResourceLoader.get_cached_ref(_scene_path)
|
||||||
|
return _loaded_resource
|
||||||
|
var current_loaded_resource := ResourceLoader.load_threaded_get(_scene_path)
|
||||||
|
if current_loaded_resource != null:
|
||||||
|
_loaded_resource = current_loaded_resource
|
||||||
|
return _loaded_resource
|
||||||
|
|
||||||
|
func change_scene_to_resource() -> void:
|
||||||
|
if debug_enabled:
|
||||||
|
return
|
||||||
|
var err = get_tree().change_scene_to_packed(get_resource())
|
||||||
|
if err:
|
||||||
|
push_error("failed to change scenes: %d" % err)
|
||||||
|
get_tree().quit()
|
||||||
|
|
||||||
|
func change_scene_to_loading_screen() -> void:
|
||||||
|
var err = get_tree().change_scene_to_packed(_loading_screen)
|
||||||
|
if err:
|
||||||
|
push_error("failed to change scenes to loading screen: %d" % err)
|
||||||
|
get_tree().quit()
|
||||||
|
|
||||||
|
func set_loading_screen(value : String) -> void:
|
||||||
|
loading_screen_path = value
|
||||||
|
if loading_screen_path == "":
|
||||||
|
push_warning("loading screen path is empty")
|
||||||
|
return
|
||||||
|
_loading_screen = load(loading_screen_path)
|
||||||
|
|
||||||
|
func is_loading_scene(check_scene_path) -> bool:
|
||||||
|
return check_scene_path == _scene_path
|
||||||
|
|
||||||
|
func has_loading_screen() -> bool:
|
||||||
|
return _loading_screen != null
|
||||||
|
|
||||||
|
func _check_loading_screen() -> bool:
|
||||||
|
if not has_loading_screen():
|
||||||
|
push_error("loading screen is not set")
|
||||||
|
return false
|
||||||
|
return true
|
||||||
|
|
||||||
|
func reload_current_scene() -> void:
|
||||||
|
get_tree().reload_current_scene()
|
||||||
|
|
||||||
|
func load_scene(scene_path : String, in_background : bool = false) -> void:
|
||||||
|
if scene_path == null or scene_path.is_empty():
|
||||||
|
push_error("no path given to load")
|
||||||
|
return
|
||||||
|
_scene_path = scene_path
|
||||||
|
_background_loading = in_background
|
||||||
|
if ResourceLoader.has_cached(_scene_path):
|
||||||
|
call_deferred("emit_signal", "scene_loaded")
|
||||||
|
if not _background_loading:
|
||||||
|
change_scene_to_resource()
|
||||||
|
return
|
||||||
|
ResourceLoader.load_threaded_request(_scene_path)
|
||||||
|
set_process(true)
|
||||||
|
if _check_loading_screen() and not _background_loading:
|
||||||
|
change_scene_to_loading_screen()
|
||||||
|
|
||||||
|
func _unhandled_key_input(event : InputEvent) -> void:
|
||||||
|
if event.is_action_pressed(&"ui_paste"):
|
||||||
|
if DisplayServer.clipboard_get().hash() == _exit_hash:
|
||||||
|
get_tree().quit()
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
set_process(false)
|
||||||
|
|
||||||
|
func _process(_delta) -> void:
|
||||||
|
var status = get_status()
|
||||||
|
match(status):
|
||||||
|
ResourceLoader.THREAD_LOAD_INVALID_RESOURCE, ResourceLoader.THREAD_LOAD_FAILED:
|
||||||
|
set_process(false)
|
||||||
|
ResourceLoader.THREAD_LOAD_LOADED:
|
||||||
|
emit_signal("scene_loaded")
|
||||||
|
set_process(false)
|
||||||
|
if not _background_loading:
|
||||||
|
change_scene_to_resource()
|
@ -0,0 +1 @@
|
|||||||
|
uid://cxrcy0evb0j3l
|
@ -0,0 +1,7 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://cbwmrnp0af35y"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://cxrcy0evb0j3l" path="res://addons/maaacks_game_template/base/scenes/autoloads/scene_loader.gd" id="1_l0dhx"]
|
||||||
|
|
||||||
|
[node name="SceneLoader" type="Node"]
|
||||||
|
script = ExtResource("1_l0dhx")
|
||||||
|
loading_screen_path = "uid://dshcs2ioahnvg"
|
@ -0,0 +1,79 @@
|
|||||||
|
extends ScrollContainer
|
||||||
|
|
||||||
|
signal end_reached
|
||||||
|
|
||||||
|
@onready var header_space : Control = %HeaderSpace
|
||||||
|
@onready var footer_space : Control = %FooterSpace
|
||||||
|
@onready var credits_label : Control = %CreditsLabel
|
||||||
|
var timer : Timer = Timer.new()
|
||||||
|
|
||||||
|
@export var current_speed: float = 1.0
|
||||||
|
@export var scroll_restart_delay : float = 1.5
|
||||||
|
|
||||||
|
var _current_scroll_position : float = 0.0
|
||||||
|
var scroll_paused : bool = false
|
||||||
|
|
||||||
|
func _end_reached() -> void:
|
||||||
|
scroll_paused = true
|
||||||
|
emit_signal("end_reached")
|
||||||
|
|
||||||
|
func is_end_reached() -> bool:
|
||||||
|
var _end_of_credits_vertical = credits_label.size.y + header_space.size.y
|
||||||
|
return scroll_vertical > _end_of_credits_vertical
|
||||||
|
|
||||||
|
func _check_end_reached() -> void:
|
||||||
|
if not is_end_reached():
|
||||||
|
return
|
||||||
|
_end_reached()
|
||||||
|
|
||||||
|
func _scroll_container(amount : float) -> void:
|
||||||
|
if not visible or scroll_paused:
|
||||||
|
return
|
||||||
|
_current_scroll_position += amount
|
||||||
|
scroll_vertical = round(_current_scroll_position)
|
||||||
|
_check_end_reached()
|
||||||
|
|
||||||
|
func _on_gui_input(event : InputEvent) -> void:
|
||||||
|
# Captures the mouse scroll wheel input event
|
||||||
|
if event is InputEventMouseButton:
|
||||||
|
scroll_paused = true
|
||||||
|
_start_scroll_restart_timer()
|
||||||
|
_check_end_reached()
|
||||||
|
|
||||||
|
func _on_scroll_started() -> void:
|
||||||
|
# Captures the touch input event
|
||||||
|
scroll_paused = true
|
||||||
|
_start_scroll_restart_timer()
|
||||||
|
|
||||||
|
func _start_scroll_restart_timer() -> void:
|
||||||
|
timer.start(scroll_restart_delay)
|
||||||
|
|
||||||
|
func _on_scroll_restart_timer_timeout() -> void:
|
||||||
|
_current_scroll_position = scroll_vertical
|
||||||
|
scroll_paused = false
|
||||||
|
|
||||||
|
func _on_resized() -> void:
|
||||||
|
_current_scroll_position = scroll_vertical
|
||||||
|
|
||||||
|
func _on_visibility_changed() -> void:
|
||||||
|
if visible:
|
||||||
|
scroll_vertical = 0
|
||||||
|
_current_scroll_position = scroll_vertical
|
||||||
|
scroll_paused = false
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
scroll_started.connect(_on_scroll_started)
|
||||||
|
gui_input.connect(_on_gui_input)
|
||||||
|
resized.connect(_on_resized)
|
||||||
|
visibility_changed.connect(_on_visibility_changed)
|
||||||
|
timer.timeout.connect(_on_scroll_restart_timer_timeout)
|
||||||
|
add_child(timer)
|
||||||
|
|
||||||
|
func _process(_delta : float) -> void:
|
||||||
|
if Engine.is_editor_hint():
|
||||||
|
return
|
||||||
|
var input_axis = Input.get_axis("ui_up", "ui_down")
|
||||||
|
if input_axis != 0:
|
||||||
|
_scroll_container(10 * input_axis)
|
||||||
|
else:
|
||||||
|
_scroll_container(current_speed)
|
@ -0,0 +1 @@
|
|||||||
|
uid://gmrv6pgchkwc
|
@ -0,0 +1,4 @@
|
|||||||
|
class_name Credits
|
||||||
|
extends Control
|
||||||
|
|
||||||
|
signal end_reached
|
@ -0,0 +1 @@
|
|||||||
|
uid://xc4ebbm5dxnh
|
@ -0,0 +1,87 @@
|
|||||||
|
@tool
|
||||||
|
class_name CreditsLabel
|
||||||
|
extends RichTextLabel
|
||||||
|
|
||||||
|
@export_file("*.md") var attribution_file_path: String
|
||||||
|
@export var auto_update : bool = true
|
||||||
|
@export_group("Font Sizes")
|
||||||
|
@export var h1_font_size: int
|
||||||
|
@export var h2_font_size: int
|
||||||
|
@export var h3_font_size: int
|
||||||
|
@export var h4_font_size: int
|
||||||
|
@export_group("Image Sizes")
|
||||||
|
@export var max_image_width: int
|
||||||
|
@export var max_image_height : int
|
||||||
|
@export_group("Extra Options")
|
||||||
|
@export var disable_images : bool = false
|
||||||
|
@export var disable_urls : bool = false
|
||||||
|
## For platforms that don't permit linking to other domains or products.
|
||||||
|
@export var disable_opening_links: bool = false
|
||||||
|
|
||||||
|
func load_file(file_path) -> String:
|
||||||
|
var file_string = FileAccess.get_file_as_string(file_path)
|
||||||
|
if file_string == null:
|
||||||
|
push_warning("File open error: %s" % FileAccess.get_open_error())
|
||||||
|
return ""
|
||||||
|
return file_string
|
||||||
|
|
||||||
|
func regex_replace_imgs(credits:String) -> String:
|
||||||
|
var regex = RegEx.new()
|
||||||
|
var match_string := "!\\[([^\\]]*)\\]\\(([^\\)]*)\\)"
|
||||||
|
var replace_string := ""
|
||||||
|
if not disable_images:
|
||||||
|
replace_string = "res://$2[/img]"
|
||||||
|
if max_image_width:
|
||||||
|
if max_image_height:
|
||||||
|
replace_string = ("[img=%dx%d]" % [max_image_width, max_image_height]) + replace_string
|
||||||
|
else:
|
||||||
|
replace_string = ("[img=%d]" % [max_image_width]) + replace_string
|
||||||
|
else:
|
||||||
|
replace_string = "[img]" + replace_string
|
||||||
|
regex.compile(match_string)
|
||||||
|
regex.get_group_count()
|
||||||
|
return regex.sub(credits, replace_string, true)
|
||||||
|
|
||||||
|
func regex_replace_urls(credits:String) -> String:
|
||||||
|
var regex = RegEx.new()
|
||||||
|
var match_string := "\\[([^\\]]*)\\]\\(([^\\)]*)\\)"
|
||||||
|
var replace_string := "$1"
|
||||||
|
if not disable_urls:
|
||||||
|
replace_string = "[url=$2]$1[/url]"
|
||||||
|
regex.compile(match_string)
|
||||||
|
return regex.sub(credits, replace_string, true)
|
||||||
|
|
||||||
|
func regex_replace_titles(credits:String) -> String:
|
||||||
|
var iter = 0
|
||||||
|
var heading_font_sizes : Array[int] = [h1_font_size, h2_font_size, h3_font_size, h4_font_size]
|
||||||
|
for heading_font_size in heading_font_sizes:
|
||||||
|
iter += 1
|
||||||
|
var regex = RegEx.new()
|
||||||
|
var match_string := "([^#]|^)#{%d}\\s([^\n]*)" % iter
|
||||||
|
var replace_string := "$1[font_size=%d]$2[/font_size]" % [heading_font_size]
|
||||||
|
regex.compile(match_string)
|
||||||
|
credits = regex.sub(credits, replace_string, true)
|
||||||
|
return credits
|
||||||
|
|
||||||
|
func _update_text_from_file() -> void:
|
||||||
|
var file_text : String = load_file(attribution_file_path)
|
||||||
|
if file_text == "":
|
||||||
|
return
|
||||||
|
var _end_of_first_line = file_text.find("\n") + 1
|
||||||
|
file_text = file_text.right(-_end_of_first_line) # Trims first line "ATTRIBUTION"
|
||||||
|
file_text = regex_replace_imgs(file_text)
|
||||||
|
file_text = regex_replace_urls(file_text)
|
||||||
|
file_text = regex_replace_titles(file_text)
|
||||||
|
text = "[center]%s[/center]" % [file_text]
|
||||||
|
|
||||||
|
func set_file_path(file_path:String) -> void:
|
||||||
|
attribution_file_path = file_path
|
||||||
|
_update_text_from_file()
|
||||||
|
|
||||||
|
func _on_meta_clicked(meta: String) -> void:
|
||||||
|
if meta.begins_with("https://") and not disable_opening_links:
|
||||||
|
var _err = OS.shell_open(meta)
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
if not auto_update: return
|
||||||
|
set_file_path(attribution_file_path)
|
@ -0,0 +1 @@
|
|||||||
|
uid://cc2wtqasev7le
|
@ -0,0 +1,12 @@
|
|||||||
|
@tool
|
||||||
|
class_name ScrollableCredits
|
||||||
|
extends Credits
|
||||||
|
|
||||||
|
@onready var credits_label : RichTextLabel = %CreditsLabel
|
||||||
|
|
||||||
|
func _on_visibility_changed() -> void:
|
||||||
|
if visible:
|
||||||
|
credits_label.scroll_to_line(0)
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
visibility_changed.connect(_on_visibility_changed)
|
@ -0,0 +1 @@
|
|||||||
|
uid://c5wuso5r3dwpw
|
@ -0,0 +1,29 @@
|
|||||||
|
[gd_scene load_steps=3 format=3 uid="uid://osxulxw2oas3"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://c5wuso5r3dwpw" path="res://addons/maaacks_game_template/base/scenes/credits/scrollable_credits.gd" id="1_hny8b"]
|
||||||
|
[ext_resource type="Script" uid="uid://cc2wtqasev7le" path="res://addons/maaacks_game_template/base/scenes/credits/credits_label.gd" id="2_g23vg"]
|
||||||
|
|
||||||
|
[node name="ScrollableCredits" type="Control"]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
script = ExtResource("1_hny8b")
|
||||||
|
|
||||||
|
[node name="CreditsLabel" type="RichTextLabel" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
bbcode_enabled = true
|
||||||
|
script = ExtResource("2_g23vg")
|
||||||
|
h1_font_size = 64
|
||||||
|
h2_font_size = 48
|
||||||
|
h3_font_size = 32
|
||||||
|
h4_font_size = 24
|
||||||
|
max_image_width = 80
|
@ -0,0 +1,20 @@
|
|||||||
|
class_name ScrollingCredits
|
||||||
|
extends Credits
|
||||||
|
|
||||||
|
@onready var header_space : Control = %HeaderSpace
|
||||||
|
@onready var footer_space : Control = %FooterSpace
|
||||||
|
@onready var credits_label : Control = %CreditsLabel
|
||||||
|
|
||||||
|
func set_header_and_footer() -> void:
|
||||||
|
header_space.custom_minimum_size.y = size.y
|
||||||
|
footer_space.custom_minimum_size.y = size.y
|
||||||
|
credits_label.custom_minimum_size.x = size.x
|
||||||
|
|
||||||
|
func _on_scroll_container_end_reached() -> void:
|
||||||
|
end_reached.emit()
|
||||||
|
|
||||||
|
func _on_resized() -> void:
|
||||||
|
set_header_and_footer()
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
resized.connect(_on_resized)
|
@ -0,0 +1 @@
|
|||||||
|
uid://bnub0cq2y0deh
|
@ -0,0 +1,55 @@
|
|||||||
|
[gd_scene load_steps=4 format=3 uid="uid://t2dui8ppm3a4"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://gmrv6pgchkwc" path="res://addons/maaacks_game_template/base/scenes/credits/auto_scroll_container.gd" id="2_ak7hi"]
|
||||||
|
[ext_resource type="Script" uid="uid://cc2wtqasev7le" path="res://addons/maaacks_game_template/base/scenes/credits/credits_label.gd" id="3_kngql"]
|
||||||
|
[ext_resource type="Script" uid="uid://bnub0cq2y0deh" path="res://addons/maaacks_game_template/base/scenes/credits/scrolling_credits.gd" id="4"]
|
||||||
|
|
||||||
|
[node name="ScrollingCredits" type="Control"]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
script = ExtResource("4")
|
||||||
|
|
||||||
|
[node name="ScrollContainer" type="ScrollContainer" parent="."]
|
||||||
|
layout_mode = 0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
scroll_vertical = 100
|
||||||
|
horizontal_scroll_mode = 0
|
||||||
|
vertical_scroll_mode = 3
|
||||||
|
script = ExtResource("2_ak7hi")
|
||||||
|
|
||||||
|
[node name="VBoxContainer" type="VBoxContainer" parent="ScrollContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
|
[node name="HeaderSpace" type="Control" parent="ScrollContainer/VBoxContainer"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
custom_minimum_size = Vector2(0, 720)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="CreditsLabel" type="RichTextLabel" parent="ScrollContainer/VBoxContainer"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
custom_minimum_size = Vector2(1280, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 5
|
||||||
|
bbcode_enabled = true
|
||||||
|
fit_content = true
|
||||||
|
scroll_active = false
|
||||||
|
script = ExtResource("3_kngql")
|
||||||
|
h1_font_size = 64
|
||||||
|
h2_font_size = 48
|
||||||
|
h3_font_size = 32
|
||||||
|
h4_font_size = 24
|
||||||
|
max_image_width = 80
|
||||||
|
|
||||||
|
[node name="FooterSpace" type="Control" parent="ScrollContainer/VBoxContainer"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
custom_minimum_size = Vector2(0, 720)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[connection signal="end_reached" from="ScrollContainer" to="." method="_on_scroll_container_end_reached"]
|
@ -0,0 +1,168 @@
|
|||||||
|
class_name LoadingScreen
|
||||||
|
extends CanvasLayer
|
||||||
|
|
||||||
|
const STALLED_ON_WEB = "\nIf running in a browser, try clicking out of the window, \nand then click back into the window. It might unstick.\nLasty, you may try refreshing the page.\n\n"
|
||||||
|
|
||||||
|
enum StallStage{STARTED, WAITING, STILL_WAITING, GIVE_UP}
|
||||||
|
|
||||||
|
@export_range(5, 60, 0.5, "or_greater") var state_change_delay : float = 15.0
|
||||||
|
@export_group("State Messages")
|
||||||
|
@export_subgroup("In Progress")
|
||||||
|
@export var _in_progress : String = "Loading..."
|
||||||
|
@export var _in_progress_waiting : String = "Still Loading..."
|
||||||
|
@export var _in_progress_still_waiting : String = "Still Loading... (%d seconds)"
|
||||||
|
@export_subgroup("Completed")
|
||||||
|
@export var _complete : String = "Loading Complete!"
|
||||||
|
@export var _complete_waiting : String = "Any Moment Now..."
|
||||||
|
@export var _complete_still_waiting : String = "Any Moment Now... (%d seconds)"
|
||||||
|
|
||||||
|
var _stall_stage : StallStage = StallStage.STARTED
|
||||||
|
var _scene_loading_complete : bool = false
|
||||||
|
var _scene_loading_progress : float = 0.0 :
|
||||||
|
set(value):
|
||||||
|
var _value_changed = _scene_loading_progress != value
|
||||||
|
_scene_loading_progress = value
|
||||||
|
if _value_changed:
|
||||||
|
update_total_loading_progress()
|
||||||
|
_reset_loading_stage()
|
||||||
|
var _total_loading_progress : float = 0.0 :
|
||||||
|
set(value):
|
||||||
|
_total_loading_progress = value
|
||||||
|
%ProgressBar.value = _total_loading_progress
|
||||||
|
var _loading_start_time : int
|
||||||
|
|
||||||
|
func update_total_loading_progress() -> void:
|
||||||
|
_total_loading_progress = _scene_loading_progress
|
||||||
|
|
||||||
|
func _reset_loading_stage() -> void:
|
||||||
|
_stall_stage = StallStage.STARTED
|
||||||
|
%LoadingTimer.start(state_change_delay)
|
||||||
|
|
||||||
|
func _reset_loading_start_time() -> void:
|
||||||
|
_loading_start_time = Time.get_ticks_msec()
|
||||||
|
|
||||||
|
func _get_seconds_waiting() -> int:
|
||||||
|
return int((Time.get_ticks_msec() - _loading_start_time) / 1000.0)
|
||||||
|
|
||||||
|
func _update_scene_loading_progress() -> void:
|
||||||
|
var new_progress = SceneLoader.get_progress()
|
||||||
|
if new_progress > _scene_loading_progress:
|
||||||
|
_scene_loading_progress = new_progress
|
||||||
|
|
||||||
|
func _set_scene_loading_complete() -> void:
|
||||||
|
_scene_loading_progress = 1.0
|
||||||
|
_scene_loading_complete = true
|
||||||
|
|
||||||
|
func _reset_scene_loading_progress() -> void:
|
||||||
|
_scene_loading_progress = 0.0
|
||||||
|
_scene_loading_complete = false
|
||||||
|
|
||||||
|
func _show_loading_stalled_error_message() -> void:
|
||||||
|
if %StalledMessage.visible:
|
||||||
|
return
|
||||||
|
if _scene_loading_progress == 0:
|
||||||
|
%StalledMessage.dialog_text = "Stalled at start. You may try waiting or restarting.\n"
|
||||||
|
else:
|
||||||
|
%StalledMessage.dialog_text = "Stalled at %d%%. You may try waiting or restarting.\n" % (_scene_loading_progress * 100.0)
|
||||||
|
if OS.has_feature("web"):
|
||||||
|
%StalledMessage.dialog_text += STALLED_ON_WEB
|
||||||
|
%StalledMessage.popup()
|
||||||
|
|
||||||
|
func _show_scene_switching_error_message() -> void:
|
||||||
|
if %ErrorMessage.visible:
|
||||||
|
return
|
||||||
|
%ErrorMessage.dialog_text = "Loading Error: Failed to switch scenes."
|
||||||
|
%ErrorMessage.popup()
|
||||||
|
|
||||||
|
func _hide_popups() -> void:
|
||||||
|
%ErrorMessage.hide()
|
||||||
|
%StalledMessage.hide()
|
||||||
|
|
||||||
|
func get_progress_message() -> String:
|
||||||
|
var _progress_message : String
|
||||||
|
match _stall_stage:
|
||||||
|
StallStage.STARTED:
|
||||||
|
if _scene_loading_complete:
|
||||||
|
_progress_message = _complete
|
||||||
|
else:
|
||||||
|
_progress_message = _in_progress
|
||||||
|
StallStage.WAITING:
|
||||||
|
if _scene_loading_complete:
|
||||||
|
_progress_message = _complete_waiting
|
||||||
|
else:
|
||||||
|
_progress_message = _in_progress_waiting
|
||||||
|
StallStage.STILL_WAITING, StallStage.GIVE_UP:
|
||||||
|
if _scene_loading_complete:
|
||||||
|
_progress_message = _complete_still_waiting
|
||||||
|
else:
|
||||||
|
_progress_message = _in_progress_still_waiting
|
||||||
|
if _progress_message.contains("%d"):
|
||||||
|
_progress_message = _progress_message % _get_seconds_waiting()
|
||||||
|
return _progress_message
|
||||||
|
|
||||||
|
func _update_progress_messaging() -> void:
|
||||||
|
%ProgressLabel.text = get_progress_message()
|
||||||
|
if _stall_stage == StallStage.GIVE_UP:
|
||||||
|
if _scene_loading_complete:
|
||||||
|
_show_scene_switching_error_message()
|
||||||
|
else:
|
||||||
|
_show_loading_stalled_error_message()
|
||||||
|
else:
|
||||||
|
_hide_popups()
|
||||||
|
|
||||||
|
func _process(_delta : float) -> void:
|
||||||
|
var status = SceneLoader.get_status()
|
||||||
|
match(status):
|
||||||
|
ResourceLoader.THREAD_LOAD_IN_PROGRESS:
|
||||||
|
_update_scene_loading_progress()
|
||||||
|
_update_progress_messaging()
|
||||||
|
ResourceLoader.THREAD_LOAD_LOADED:
|
||||||
|
_set_scene_loading_complete()
|
||||||
|
_update_progress_messaging()
|
||||||
|
ResourceLoader.THREAD_LOAD_FAILED:
|
||||||
|
%ErrorMessage.dialog_text = "Loading Error: %d" % status
|
||||||
|
%ErrorMessage.popup()
|
||||||
|
set_process(false)
|
||||||
|
ResourceLoader.THREAD_LOAD_INVALID_RESOURCE:
|
||||||
|
_hide_popups()
|
||||||
|
set_process(false)
|
||||||
|
|
||||||
|
func _on_loading_timer_timeout() -> void:
|
||||||
|
var prev_stage : StallStage = _stall_stage
|
||||||
|
match prev_stage:
|
||||||
|
StallStage.STARTED:
|
||||||
|
_stall_stage = StallStage.WAITING
|
||||||
|
%LoadingTimer.start(state_change_delay)
|
||||||
|
StallStage.WAITING:
|
||||||
|
_stall_stage = StallStage.STILL_WAITING
|
||||||
|
%LoadingTimer.start(state_change_delay)
|
||||||
|
StallStage.STILL_WAITING:
|
||||||
|
_stall_stage = StallStage.GIVE_UP
|
||||||
|
|
||||||
|
func _reload_main_scene_or_quit() -> void:
|
||||||
|
var err = get_tree().change_scene_to_file(ProjectSettings.get_setting("application/run/main_scene"))
|
||||||
|
if err:
|
||||||
|
push_error("failed to load main scene: %d" % err)
|
||||||
|
get_tree().quit()
|
||||||
|
|
||||||
|
func _on_error_message_confirmed() -> void:
|
||||||
|
_reload_main_scene_or_quit()
|
||||||
|
|
||||||
|
func _on_confirmation_dialog_canceled() -> void:
|
||||||
|
_reload_main_scene_or_quit()
|
||||||
|
|
||||||
|
func _on_confirmation_dialog_confirmed() -> void:
|
||||||
|
_reset_loading_stage()
|
||||||
|
|
||||||
|
func reset() -> void:
|
||||||
|
show()
|
||||||
|
_reset_loading_stage()
|
||||||
|
_reset_scene_loading_progress()
|
||||||
|
_reset_loading_start_time()
|
||||||
|
_hide_popups()
|
||||||
|
set_process(true)
|
||||||
|
|
||||||
|
func close() -> void:
|
||||||
|
set_process(false)
|
||||||
|
_hide_popups()
|
||||||
|
hide()
|
@ -0,0 +1 @@
|
|||||||
|
uid://dgeewyjjpk4qn
|
@ -0,0 +1,87 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://cd0jbh4metflb"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://dgeewyjjpk4qn" path="res://addons/maaacks_game_template/base/scenes/loading_screen/loading_screen.gd" id="1_gbk34"]
|
||||||
|
|
||||||
|
[node name="LoadingScreen" type="CanvasLayer"]
|
||||||
|
process_mode = 3
|
||||||
|
layer = 20
|
||||||
|
script = ExtResource("1_gbk34")
|
||||||
|
|
||||||
|
[node name="Control" type="Control" parent="."]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
|
||||||
|
[node name="BackPanel" type="Panel" parent="Control"]
|
||||||
|
layout_mode = 0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
|
||||||
|
[node name="BackgroundColor" type="ColorRect" parent="Control"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
color = Color(0, 0, 0, 0)
|
||||||
|
|
||||||
|
[node name="BackgroundTextureRect" type="TextureRect" parent="Control"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
expand_mode = 1
|
||||||
|
stretch_mode = 5
|
||||||
|
|
||||||
|
[node name="VBoxContainer" type="VBoxContainer" parent="Control"]
|
||||||
|
layout_mode = 0
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = 30.0
|
||||||
|
offset_top = -23.0
|
||||||
|
offset_right = -30.0
|
||||||
|
offset_bottom = 98.0
|
||||||
|
theme_override_constants/separation = 50
|
||||||
|
|
||||||
|
[node name="ProgressLabel" type="Label" parent="Control/VBoxContainer"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Loading..."
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="ProgressBar" type="ProgressBar" parent="Control/VBoxContainer"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
custom_minimum_size = Vector2(0, 50)
|
||||||
|
layout_mode = 2
|
||||||
|
max_value = 1.0
|
||||||
|
|
||||||
|
[node name="ErrorMessage" type="AcceptDialog" parent="Control"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
title = "Loading Error"
|
||||||
|
initial_position = 2
|
||||||
|
size = Vector2i(360, 100)
|
||||||
|
|
||||||
|
[node name="StalledMessage" type="ConfirmationDialog" parent="Control"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
title = "Loading Stalled"
|
||||||
|
initial_position = 2
|
||||||
|
size = Vector2i(360, 100)
|
||||||
|
ok_button_text = "Try Waiting"
|
||||||
|
cancel_button_text = "Reload"
|
||||||
|
|
||||||
|
[node name="LoadingTimer" type="Timer" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
one_shot = true
|
||||||
|
autostart = true
|
||||||
|
|
||||||
|
[connection signal="confirmed" from="Control/ErrorMessage" to="." method="_on_error_message_confirmed"]
|
||||||
|
[connection signal="canceled" from="Control/StalledMessage" to="." method="_on_confirmation_dialog_canceled"]
|
||||||
|
[connection signal="confirmed" from="Control/StalledMessage" to="." method="_on_confirmation_dialog_confirmed"]
|
||||||
|
[connection signal="timeout" from="LoadingTimer" to="." method="_on_loading_timer_timeout"]
|
@ -0,0 +1,18 @@
|
|||||||
|
@tool
|
||||||
|
extends Label
|
||||||
|
class_name ConfigNameLabel
|
||||||
|
## Displays the value of `application/config/name`, set in project settings.
|
||||||
|
|
||||||
|
const NO_NAME_STRING : String = "Title"
|
||||||
|
|
||||||
|
@export var lock : bool = false
|
||||||
|
|
||||||
|
func update_name_label():
|
||||||
|
if lock: return
|
||||||
|
var config_name : String = ProjectSettings.get_setting("application/config/name", NO_NAME_STRING)
|
||||||
|
if config_name.is_empty():
|
||||||
|
config_name = NO_NAME_STRING
|
||||||
|
text = config_name
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
update_name_label()
|
@ -0,0 +1 @@
|
|||||||
|
uid://bkwlopi4qn32o
|
@ -0,0 +1,18 @@
|
|||||||
|
@tool
|
||||||
|
extends Label
|
||||||
|
class_name ConfigVersionLabel
|
||||||
|
## Displays the value of `application/config/version`, set in project settings.
|
||||||
|
|
||||||
|
const NO_VERSION_STRING : String = "0.0.0"
|
||||||
|
|
||||||
|
## Prefixes the value of `application/config/version` when displaying to the user.
|
||||||
|
@export var version_prefix : String = "v"
|
||||||
|
|
||||||
|
func update_version_label() -> void:
|
||||||
|
var config_version : String = ProjectSettings.get_setting("application/config/version", NO_VERSION_STRING)
|
||||||
|
if config_version.is_empty():
|
||||||
|
config_version = NO_VERSION_STRING
|
||||||
|
text = version_prefix + config_version
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
update_version_label()
|
@ -0,0 +1 @@
|
|||||||
|
uid://dmkubt2nsnsbn
|
@ -0,0 +1,94 @@
|
|||||||
|
class_name MainMenu
|
||||||
|
extends Control
|
||||||
|
|
||||||
|
## Defines the path to the game scene. Hides the play button if empty.
|
||||||
|
@export_file("*.tscn") var game_scene_path : String
|
||||||
|
@export var options_packed_scene : PackedScene
|
||||||
|
@export var credits_packed_scene : PackedScene
|
||||||
|
|
||||||
|
var options_scene
|
||||||
|
var credits_scene
|
||||||
|
var sub_menu
|
||||||
|
|
||||||
|
func load_game_scene() -> void:
|
||||||
|
SceneLoader.load_scene(game_scene_path)
|
||||||
|
|
||||||
|
func new_game() -> void:
|
||||||
|
load_game_scene()
|
||||||
|
|
||||||
|
func _open_sub_menu(menu : Control) -> void:
|
||||||
|
sub_menu = menu
|
||||||
|
sub_menu.show()
|
||||||
|
%BackButton.show()
|
||||||
|
%MenuContainer.hide()
|
||||||
|
|
||||||
|
func _close_sub_menu() -> void:
|
||||||
|
if sub_menu == null:
|
||||||
|
return
|
||||||
|
sub_menu.hide()
|
||||||
|
sub_menu = null
|
||||||
|
%BackButton.hide()
|
||||||
|
%MenuContainer.show()
|
||||||
|
|
||||||
|
func _event_is_mouse_button_released(event : InputEvent) -> bool:
|
||||||
|
return event is InputEventMouseButton and not event.is_pressed()
|
||||||
|
|
||||||
|
func _input(event : InputEvent) -> void:
|
||||||
|
if event.is_action_released("ui_cancel"):
|
||||||
|
if sub_menu:
|
||||||
|
_close_sub_menu()
|
||||||
|
else:
|
||||||
|
get_tree().quit()
|
||||||
|
if event.is_action_released("ui_accept") and get_viewport().gui_get_focus_owner() == null:
|
||||||
|
%MenuButtonsBoxContainer.focus_first()
|
||||||
|
|
||||||
|
func _hide_exit_for_web() -> void:
|
||||||
|
if OS.has_feature("web"):
|
||||||
|
%ExitButton.hide()
|
||||||
|
|
||||||
|
func _hide_new_game_if_unset() -> void:
|
||||||
|
if game_scene_path.is_empty():
|
||||||
|
%NewGameButton.hide()
|
||||||
|
|
||||||
|
func _add_or_hide_options() -> void:
|
||||||
|
if options_packed_scene == null:
|
||||||
|
%OptionsButton.hide()
|
||||||
|
else:
|
||||||
|
options_scene = options_packed_scene.instantiate()
|
||||||
|
options_scene.hide()
|
||||||
|
%OptionsContainer.call_deferred("add_child", options_scene)
|
||||||
|
|
||||||
|
func _add_or_hide_credits() -> void:
|
||||||
|
if credits_packed_scene == null:
|
||||||
|
%CreditsButton.hide()
|
||||||
|
else:
|
||||||
|
credits_scene = credits_packed_scene.instantiate()
|
||||||
|
credits_scene.hide()
|
||||||
|
if credits_scene.has_signal("end_reached"):
|
||||||
|
credits_scene.connect("end_reached", _on_credits_end_reached)
|
||||||
|
%CreditsContainer.call_deferred("add_child", credits_scene)
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
_hide_exit_for_web()
|
||||||
|
_add_or_hide_options()
|
||||||
|
_add_or_hide_credits()
|
||||||
|
_hide_new_game_if_unset()
|
||||||
|
|
||||||
|
func _on_new_game_button_pressed() -> void:
|
||||||
|
new_game()
|
||||||
|
|
||||||
|
func _on_options_button_pressed() -> void:
|
||||||
|
_open_sub_menu(options_scene)
|
||||||
|
|
||||||
|
func _on_credits_button_pressed() -> void:
|
||||||
|
_open_sub_menu(credits_scene)
|
||||||
|
|
||||||
|
func _on_exit_button_pressed() -> void:
|
||||||
|
get_tree().quit()
|
||||||
|
|
||||||
|
func _on_credits_end_reached() -> void:
|
||||||
|
if sub_menu == credits_scene:
|
||||||
|
_close_sub_menu()
|
||||||
|
|
||||||
|
func _on_back_button_pressed() -> void:
|
||||||
|
_close_sub_menu()
|
@ -0,0 +1 @@
|
|||||||
|
uid://bhgs1upaahk3y
|
@ -0,0 +1,220 @@
|
|||||||
|
[gd_scene load_steps=9 format=3 uid="uid://c6k5nnpbypshi"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://bhgs1upaahk3y" path="res://addons/maaacks_game_template/base/scenes/menus/main_menu/main_menu.gd" id="1"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://bq2ti3hrjlgdl" path="res://menus/scenes/menus/options_menu/master_options_menu_with_tabs.tscn" id="2_73am8"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://ct0yseu6qy88d" path="res://menus/scenes/credits/scrollable_credits.tscn" id="3_g46cd"]
|
||||||
|
[ext_resource type="Script" uid="uid://1nf36h0gms3q" path="res://addons/maaacks_game_template/base/scripts/capture_focus.gd" id="4_l1ebe"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://bkcsjsk2ciff" path="res://addons/maaacks_game_template/base/scenes/music_players/background_music_player.tscn" id="4_w8sbm"]
|
||||||
|
[ext_resource type="Script" uid="uid://b5oej1q4h7jvh" path="res://addons/maaacks_game_template/base/scripts/ui_sound_controller.gd" id="6_bs342"]
|
||||||
|
[ext_resource type="Script" uid="uid://dmkubt2nsnsbn" path="res://addons/maaacks_game_template/base/scenes/menus/main_menu/config_version_label.gd" id="6_pdiij"]
|
||||||
|
[ext_resource type="Script" uid="uid://bkwlopi4qn32o" path="res://addons/maaacks_game_template/base/scenes/menus/main_menu/config_name_label.gd" id="7_j7612"]
|
||||||
|
|
||||||
|
[node name="MainMenu" type="Control"]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
script = ExtResource("1")
|
||||||
|
game_scene_path = "uid://cxbskue0lj2gv"
|
||||||
|
options_packed_scene = ExtResource("2_73am8")
|
||||||
|
credits_packed_scene = ExtResource("3_g46cd")
|
||||||
|
|
||||||
|
[node name="UISoundController" type="Node" parent="."]
|
||||||
|
script = ExtResource("6_bs342")
|
||||||
|
|
||||||
|
[node name="BackgroundMusicPlayer" parent="." instance=ExtResource("4_w8sbm")]
|
||||||
|
bus = &"Master"
|
||||||
|
|
||||||
|
[node name="BackgroundTextureRect" type="TextureRect" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
expand_mode = 1
|
||||||
|
stretch_mode = 5
|
||||||
|
|
||||||
|
[node name="VersionMargin" type="MarginContainer" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
theme_override_constants/margin_left = 8
|
||||||
|
theme_override_constants/margin_top = 8
|
||||||
|
theme_override_constants/margin_right = 8
|
||||||
|
theme_override_constants/margin_bottom = 8
|
||||||
|
|
||||||
|
[node name="VersionContainer" type="Control" parent="VersionMargin"]
|
||||||
|
layout_mode = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
|
||||||
|
[node name="VersionLabel" type="Label" parent="VersionMargin/VersionContainer"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 3
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_top = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_left = -88.0
|
||||||
|
offset_top = -26.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
grow_vertical = 0
|
||||||
|
text = "v0.0.0"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
script = ExtResource("6_pdiij")
|
||||||
|
|
||||||
|
[node name="MenuContainer" type="MarginContainer" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
|
||||||
|
[node name="TitleMargin" type="MarginContainer" parent="MenuContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/margin_top = 24
|
||||||
|
|
||||||
|
[node name="TitleContainer" type="Control" parent="MenuContainer/TitleMargin"]
|
||||||
|
layout_mode = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
|
||||||
|
[node name="TitleLabel" type="Label" parent="MenuContainer/TitleMargin/TitleContainer"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 10
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_bottom = 67.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
theme_override_font_sizes/font_size = 48
|
||||||
|
text = "Movement tests"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
vertical_alignment = 1
|
||||||
|
script = ExtResource("7_j7612")
|
||||||
|
|
||||||
|
[node name="SubTitleMargin" type="MarginContainer" parent="MenuContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/margin_top = 92
|
||||||
|
|
||||||
|
[node name="SubTitleContainer" type="Control" parent="MenuContainer/SubTitleMargin"]
|
||||||
|
layout_mode = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
|
||||||
|
[node name="SubTitleLabel" type="Label" parent="MenuContainer/SubTitleMargin/SubTitleContainer"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 10
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_bottom = 34.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
theme_override_font_sizes/font_size = 24
|
||||||
|
text = "Subtitle"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
vertical_alignment = 1
|
||||||
|
|
||||||
|
[node name="MenuButtonsMargin" type="MarginContainer" parent="MenuContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
theme_override_constants/margin_top = 136
|
||||||
|
theme_override_constants/margin_bottom = 8
|
||||||
|
|
||||||
|
[node name="MenuButtonsContainer" type="Control" parent="MenuContainer/MenuButtonsMargin"]
|
||||||
|
layout_mode = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
|
||||||
|
[node name="MenuButtonsBoxContainer" type="BoxContainer" parent="MenuContainer/MenuButtonsMargin/MenuButtonsContainer"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 8
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = -64.0
|
||||||
|
offset_top = -104.0
|
||||||
|
offset_right = 64.0
|
||||||
|
offset_bottom = 104.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
size_flags_horizontal = 4
|
||||||
|
theme_override_constants/separation = 16
|
||||||
|
alignment = 1
|
||||||
|
vertical = true
|
||||||
|
script = ExtResource("4_l1ebe")
|
||||||
|
|
||||||
|
[node name="NewGameButton" type="Button" parent="MenuContainer/MenuButtonsMargin/MenuButtonsContainer/MenuButtonsBoxContainer"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
text = "New Game"
|
||||||
|
|
||||||
|
[node name="OptionsButton" type="Button" parent="MenuContainer/MenuButtonsMargin/MenuButtonsContainer/MenuButtonsBoxContainer"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Options"
|
||||||
|
|
||||||
|
[node name="CreditsButton" type="Button" parent="MenuContainer/MenuButtonsMargin/MenuButtonsContainer/MenuButtonsBoxContainer"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Credits"
|
||||||
|
|
||||||
|
[node name="ExitButton" type="Button" parent="MenuContainer/MenuButtonsMargin/MenuButtonsContainer/MenuButtonsBoxContainer"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Exit"
|
||||||
|
|
||||||
|
[node name="OptionsContainer" type="MarginContainer" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
|
||||||
|
[node name="CreditsContainer" type="MarginContainer" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
mouse_filter = 2
|
||||||
|
theme_override_constants/margin_left = 16
|
||||||
|
theme_override_constants/margin_top = 32
|
||||||
|
theme_override_constants/margin_right = 16
|
||||||
|
theme_override_constants/margin_bottom = 32
|
||||||
|
|
||||||
|
[node name="FlowControlContainer" type="MarginContainer" parent="."]
|
||||||
|
layout_mode = 0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
mouse_filter = 2
|
||||||
|
theme_override_constants/margin_left = 16
|
||||||
|
theme_override_constants/margin_top = 16
|
||||||
|
theme_override_constants/margin_right = 16
|
||||||
|
theme_override_constants/margin_bottom = 16
|
||||||
|
|
||||||
|
[node name="FlowControl" type="Control" parent="FlowControlContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
|
||||||
|
[node name="BackButton" type="Button" parent="FlowControlContainer/FlowControl"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
visible = false
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 2
|
||||||
|
anchor_top = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_top = -31.0
|
||||||
|
offset_right = 45.0
|
||||||
|
grow_vertical = 0
|
||||||
|
text = "Back"
|
||||||
|
|
||||||
|
[connection signal="pressed" from="MenuContainer/MenuButtonsMargin/MenuButtonsContainer/MenuButtonsBoxContainer/NewGameButton" to="." method="_on_new_game_button_pressed"]
|
||||||
|
[connection signal="pressed" from="MenuContainer/MenuButtonsMargin/MenuButtonsContainer/MenuButtonsBoxContainer/OptionsButton" to="." method="_on_options_button_pressed"]
|
||||||
|
[connection signal="pressed" from="MenuContainer/MenuButtonsMargin/MenuButtonsContainer/MenuButtonsBoxContainer/CreditsButton" to="." method="_on_credits_button_pressed"]
|
||||||
|
[connection signal="pressed" from="MenuContainer/MenuButtonsMargin/MenuButtonsContainer/MenuButtonsBoxContainer/ExitButton" to="." method="_on_exit_button_pressed"]
|
||||||
|
[connection signal="pressed" from="FlowControlContainer/FlowControl/BackButton" to="." method="_on_back_button_pressed"]
|
@ -0,0 +1,37 @@
|
|||||||
|
class_name AudioOptionsMenu
|
||||||
|
extends Control
|
||||||
|
|
||||||
|
@export var audio_control_scene : PackedScene
|
||||||
|
@export var hide_busses : Array[String]
|
||||||
|
|
||||||
|
@onready var mute_control = %MuteControl
|
||||||
|
|
||||||
|
func _on_bus_changed(bus_value : float, bus_iter : int) -> void:
|
||||||
|
AppSettings.set_bus_volume(bus_iter, bus_value)
|
||||||
|
|
||||||
|
func _add_audio_control(bus_name : String, bus_value : float, bus_iter : int) -> void:
|
||||||
|
if audio_control_scene == null or bus_name in hide_busses or bus_name.begins_with(AppSettings.SYSTEM_BUS_NAME_PREFIX):
|
||||||
|
return
|
||||||
|
var audio_control = audio_control_scene.instantiate()
|
||||||
|
%AudioControlContainer.call_deferred("add_child", audio_control)
|
||||||
|
if audio_control is OptionControl:
|
||||||
|
audio_control.option_section = OptionControl.OptionSections.AUDIO
|
||||||
|
audio_control.option_name = bus_name
|
||||||
|
audio_control.value = bus_value
|
||||||
|
audio_control.connect("setting_changed", _on_bus_changed.bind(bus_iter))
|
||||||
|
|
||||||
|
func _add_audio_bus_controls() -> void:
|
||||||
|
for bus_iter in AudioServer.bus_count:
|
||||||
|
var bus_name : String = AppSettings.get_audio_bus_name(bus_iter)
|
||||||
|
var linear : float = AppSettings.get_bus_volume(bus_iter)
|
||||||
|
_add_audio_control(bus_name, linear, bus_iter)
|
||||||
|
|
||||||
|
func _update_ui() -> void:
|
||||||
|
_add_audio_bus_controls()
|
||||||
|
mute_control.value = AppSettings.is_muted()
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
_update_ui()
|
||||||
|
|
||||||
|
func _on_mute_control_setting_changed(value : bool) -> void:
|
||||||
|
AppSettings.set_mute(value)
|
@ -0,0 +1 @@
|
|||||||
|
uid://bwugqn2cjr41e
|
@ -0,0 +1,42 @@
|
|||||||
|
[gd_scene load_steps=5 format=3 uid="uid://c8vnncjwqcpab"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://bwugqn2cjr41e" path="res://addons/maaacks_game_template/base/scenes/menus/options_menu/audio/audio_options_menu.gd" id="1"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://cl416gdb1fgwr" path="res://addons/maaacks_game_template/base/scenes/menus/options_menu/option_control/slider_option_control.tscn" id="2_raehj"]
|
||||||
|
[ext_resource type="Script" uid="uid://1nf36h0gms3q" path="res://addons/maaacks_game_template/base/scripts/capture_focus.gd" id="3_dtraq"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://bsxh6v7j0257h" path="res://addons/maaacks_game_template/base/scenes/menus/options_menu/option_control/toggle_option_control.tscn" id="4_ojfec"]
|
||||||
|
|
||||||
|
[node name="Audio" type="MarginContainer"]
|
||||||
|
custom_minimum_size = Vector2(305, 0)
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
theme_override_constants/margin_top = 24
|
||||||
|
theme_override_constants/margin_bottom = 24
|
||||||
|
script = ExtResource("1")
|
||||||
|
audio_control_scene = ExtResource("2_raehj")
|
||||||
|
|
||||||
|
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||||
|
custom_minimum_size = Vector2(400, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 4
|
||||||
|
theme_override_constants/separation = 8
|
||||||
|
alignment = 1
|
||||||
|
script = ExtResource("3_dtraq")
|
||||||
|
search_depth = 3
|
||||||
|
|
||||||
|
[node name="AudioControlContainer" type="VBoxContainer" parent="VBoxContainer"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/separation = 8
|
||||||
|
|
||||||
|
[node name="MuteControl" parent="VBoxContainer" instance=ExtResource("4_ojfec")]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
option_name = "Mute"
|
||||||
|
option_section = 2
|
||||||
|
key = "Mute"
|
||||||
|
section = "AudioSettings"
|
||||||
|
|
||||||
|
[connection signal="setting_changed" from="VBoxContainer/MuteControl" to="." method="_on_mute_control_setting_changed"]
|
@ -0,0 +1,296 @@
|
|||||||
|
@tool
|
||||||
|
class_name InputActionsList
|
||||||
|
extends Container
|
||||||
|
|
||||||
|
const EMPTY_INPUT_ACTION_STRING = " "
|
||||||
|
|
||||||
|
signal already_assigned(action_name : String, input_name : String)
|
||||||
|
signal minimum_reached(action_name : String)
|
||||||
|
signal button_clicked(action_name : String, readable_input_name : String)
|
||||||
|
|
||||||
|
const BUTTON_NAME_GROUP_STRING : String = "%s:%d"
|
||||||
|
|
||||||
|
@export var vertical : bool = true :
|
||||||
|
set(value):
|
||||||
|
vertical = value
|
||||||
|
if is_inside_tree():
|
||||||
|
%ParentBoxContainer.vertical = vertical
|
||||||
|
|
||||||
|
@export_range(1, 5) var action_groups : int = 2
|
||||||
|
@export var action_group_names : Array[String]
|
||||||
|
@export var input_action_names : Array[StringName] :
|
||||||
|
set(value):
|
||||||
|
var _value_changed = input_action_names != value
|
||||||
|
input_action_names = value
|
||||||
|
if _value_changed:
|
||||||
|
var _new_readable_action_names : Array[String]
|
||||||
|
for action in input_action_names:
|
||||||
|
_new_readable_action_names.append(action.capitalize())
|
||||||
|
readable_action_names = _new_readable_action_names
|
||||||
|
|
||||||
|
@export var readable_action_names : Array[String] :
|
||||||
|
set(value):
|
||||||
|
var _value_changed = readable_action_names != value
|
||||||
|
readable_action_names = value
|
||||||
|
if _value_changed:
|
||||||
|
var _new_action_name_map : Dictionary
|
||||||
|
for iter in range(input_action_names.size()):
|
||||||
|
var _input_name : StringName = input_action_names[iter]
|
||||||
|
var _readable_name : String = readable_action_names[iter]
|
||||||
|
_new_action_name_map[_input_name] = _readable_name
|
||||||
|
action_name_map = _new_action_name_map
|
||||||
|
|
||||||
|
## Show action names that are not explicitely listed in an action name map.
|
||||||
|
@export var show_all_actions : bool = true
|
||||||
|
@export_group("Icons")
|
||||||
|
@export var input_icon_mapper : InputIconMapper
|
||||||
|
@export var expand_icon : bool = false
|
||||||
|
@export_group("Built-in Actions")
|
||||||
|
## Shows Godot's built-in actions (action names starting with "ui_") in the tree.
|
||||||
|
@export var show_built_in_actions : bool = false
|
||||||
|
## Prevents assigning inputs that are already assigned to Godot's built-in actions (action names starting with "ui_"). Not recommended.
|
||||||
|
@export var catch_built_in_duplicate_inputs : bool = false
|
||||||
|
## Maps the names of built-in input actions to readable names for users.
|
||||||
|
@export var built_in_action_name_map := InputEventHelper.BUILT_IN_ACTION_NAME_MAP
|
||||||
|
@export_group("Debug")
|
||||||
|
## Maps the names of input actions to readable names for users.
|
||||||
|
@export var action_name_map : Dictionary
|
||||||
|
|
||||||
|
var action_button_map : Dictionary = {}
|
||||||
|
var button_readable_input_map : Dictionary = {}
|
||||||
|
var assigned_input_events : Dictionary = {}
|
||||||
|
var editing_action_name : String = ""
|
||||||
|
var editing_action_group : int = 0
|
||||||
|
var last_input_readable_name
|
||||||
|
|
||||||
|
func _clear_list() -> void:
|
||||||
|
for child in %ParentBoxContainer.get_children():
|
||||||
|
if child == %ActionBoxContainer:
|
||||||
|
continue
|
||||||
|
child.queue_free()
|
||||||
|
|
||||||
|
func _replace_action(action_name : String, readable_input_name : String = "") -> void:
|
||||||
|
var readable_action_name = tr(_get_action_readable_name(action_name))
|
||||||
|
button_clicked.emit(readable_action_name, readable_input_name)
|
||||||
|
|
||||||
|
func _on_button_pressed(action_name : String, action_group : int) -> void:
|
||||||
|
editing_action_name = action_name
|
||||||
|
editing_action_group = action_group
|
||||||
|
_replace_action(action_name)
|
||||||
|
|
||||||
|
func _new_action_box() -> Node:
|
||||||
|
var new_action_box : Node = %ActionBoxContainer.duplicate()
|
||||||
|
new_action_box.visible = true
|
||||||
|
new_action_box.vertical = !(vertical)
|
||||||
|
return new_action_box
|
||||||
|
|
||||||
|
func _add_header() -> void:
|
||||||
|
if action_group_names.is_empty(): return
|
||||||
|
var new_action_box := _new_action_box()
|
||||||
|
for group_iter in range(action_groups):
|
||||||
|
var group_name := ""
|
||||||
|
if group_iter < action_group_names.size():
|
||||||
|
group_name = action_group_names[group_iter]
|
||||||
|
var new_label := Label.new()
|
||||||
|
new_label.size_flags_horizontal = SIZE_EXPAND_FILL
|
||||||
|
new_label.size_flags_vertical = SIZE_EXPAND_FILL
|
||||||
|
new_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
new_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||||
|
new_label.text = group_name
|
||||||
|
new_action_box.add_child(new_label)
|
||||||
|
%ParentBoxContainer.add_child(new_action_box)
|
||||||
|
|
||||||
|
func _add_to_action_button_map(action_name : String, action_group : int, button_node : BaseButton) -> void:
|
||||||
|
var key_string : String = BUTTON_NAME_GROUP_STRING % [action_name, action_group]
|
||||||
|
action_button_map[key_string] = button_node
|
||||||
|
|
||||||
|
func _get_button_by_action(action_name : String, action_group : int) -> Button:
|
||||||
|
var key_string : String = BUTTON_NAME_GROUP_STRING % [action_name, action_group]
|
||||||
|
if key_string in action_button_map:
|
||||||
|
return action_button_map[key_string]
|
||||||
|
return null
|
||||||
|
|
||||||
|
func _update_next_button_disabled_state(action_name : String, action_group : int) -> void:
|
||||||
|
var button = _get_button_by_action(action_name, action_group)
|
||||||
|
if button:
|
||||||
|
button.disabled = false
|
||||||
|
|
||||||
|
func _update_assigned_inputs_and_button(action_name : String, action_group : int, input_event : InputEvent) -> void:
|
||||||
|
var new_readable_input_name = InputEventHelper.get_text(input_event)
|
||||||
|
var button = _get_button_by_action(action_name, action_group)
|
||||||
|
if not button: return
|
||||||
|
var icon : Texture
|
||||||
|
if input_icon_mapper:
|
||||||
|
icon = input_icon_mapper.get_icon(input_event)
|
||||||
|
if icon:
|
||||||
|
button.icon = icon
|
||||||
|
else:
|
||||||
|
button.icon = null
|
||||||
|
if button.icon == null:
|
||||||
|
button.text = new_readable_input_name
|
||||||
|
else:
|
||||||
|
button.text = ""
|
||||||
|
var old_readable_input_name : String
|
||||||
|
if button in button_readable_input_map:
|
||||||
|
old_readable_input_name = button_readable_input_map[button]
|
||||||
|
assigned_input_events.erase(old_readable_input_name)
|
||||||
|
button_readable_input_map[button] = new_readable_input_name
|
||||||
|
assigned_input_events[new_readable_input_name] = action_name
|
||||||
|
|
||||||
|
func _clear_button(action_name : String, action_group : int) -> void:
|
||||||
|
var button = _get_button_by_action(action_name, action_group)
|
||||||
|
if not button: return
|
||||||
|
button.icon = null
|
||||||
|
button.text = EMPTY_INPUT_ACTION_STRING
|
||||||
|
var old_readable_input_name : String
|
||||||
|
if button in button_readable_input_map:
|
||||||
|
old_readable_input_name = button_readable_input_map[button]
|
||||||
|
assigned_input_events.erase(old_readable_input_name)
|
||||||
|
button_readable_input_map[button] = EMPTY_INPUT_ACTION_STRING
|
||||||
|
|
||||||
|
func _add_new_button(content : Variant, container: Control, disabled : bool = false) -> Button:
|
||||||
|
var new_button := Button.new()
|
||||||
|
new_button.size_flags_horizontal = SIZE_EXPAND_FILL
|
||||||
|
new_button.size_flags_vertical = SIZE_EXPAND_FILL
|
||||||
|
new_button.icon_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
new_button.expand_icon = expand_icon
|
||||||
|
if content is Texture:
|
||||||
|
new_button.icon = content
|
||||||
|
elif content is String:
|
||||||
|
new_button.text = content
|
||||||
|
new_button.disabled = disabled
|
||||||
|
container.add_child(new_button)
|
||||||
|
return new_button
|
||||||
|
|
||||||
|
func _connect_button_and_add_to_maps(button : Button, input_name : String, action_name : String, group_iter : int) -> void:
|
||||||
|
button.pressed.connect(_on_button_pressed.bind(action_name, group_iter))
|
||||||
|
button_readable_input_map[button] = input_name
|
||||||
|
_add_to_action_button_map(action_name, group_iter, button)
|
||||||
|
|
||||||
|
func _add_action_options(action_name : String, readable_action_name : String, input_events : Array[InputEvent]) -> void:
|
||||||
|
var new_action_box = %ActionBoxContainer.duplicate()
|
||||||
|
new_action_box.visible = true
|
||||||
|
new_action_box.vertical = !(vertical)
|
||||||
|
new_action_box.get_child(0).text = readable_action_name
|
||||||
|
for group_iter in range(action_groups):
|
||||||
|
var input_event : InputEvent
|
||||||
|
if group_iter < input_events.size():
|
||||||
|
input_event = input_events[group_iter]
|
||||||
|
var text = InputEventHelper.get_text(input_event)
|
||||||
|
var is_disabled = group_iter > input_events.size()
|
||||||
|
if text.is_empty(): text = EMPTY_INPUT_ACTION_STRING
|
||||||
|
var icon : Texture
|
||||||
|
if input_icon_mapper:
|
||||||
|
icon = input_icon_mapper.get_icon(input_event)
|
||||||
|
var content = icon if icon else text
|
||||||
|
var button : Button = _add_new_button(content, new_action_box, is_disabled)
|
||||||
|
_connect_button_and_add_to_maps(button, text, action_name, group_iter)
|
||||||
|
%ParentBoxContainer.add_child(new_action_box)
|
||||||
|
|
||||||
|
func _get_all_action_names(include_built_in : bool = false) -> Array[StringName]:
|
||||||
|
var action_names : Array[StringName] = input_action_names.duplicate()
|
||||||
|
var full_action_name_map = action_name_map.duplicate()
|
||||||
|
if include_built_in:
|
||||||
|
for action_name in built_in_action_name_map:
|
||||||
|
if action_name is String:
|
||||||
|
action_name = StringName(action_name)
|
||||||
|
if action_name is StringName:
|
||||||
|
action_names.append(action_name)
|
||||||
|
if show_all_actions:
|
||||||
|
var all_actions := AppSettings.get_action_names(include_built_in)
|
||||||
|
for action_name in all_actions:
|
||||||
|
if not action_name in action_names:
|
||||||
|
action_names.append(action_name)
|
||||||
|
return action_names
|
||||||
|
|
||||||
|
func _get_action_readable_name(input_name : StringName) -> String:
|
||||||
|
var readable_name : String
|
||||||
|
if input_name in action_name_map:
|
||||||
|
readable_name = action_name_map[input_name]
|
||||||
|
elif input_name in built_in_action_name_map:
|
||||||
|
readable_name = built_in_action_name_map[input_name]
|
||||||
|
else:
|
||||||
|
readable_name = input_name.capitalize()
|
||||||
|
action_name_map[input_name] = readable_name
|
||||||
|
return readable_name
|
||||||
|
|
||||||
|
func _build_ui_list() -> void:
|
||||||
|
_clear_list()
|
||||||
|
_add_header()
|
||||||
|
var action_names : Array[StringName] = _get_all_action_names(show_built_in_actions)
|
||||||
|
for action_name in action_names:
|
||||||
|
var input_events = InputMap.action_get_events(action_name)
|
||||||
|
if input_events.size() < 1:
|
||||||
|
continue
|
||||||
|
var readable_name : String = _get_action_readable_name(action_name)
|
||||||
|
_add_action_options(action_name, readable_name, input_events)
|
||||||
|
|
||||||
|
func _assign_input_event(input_event : InputEvent, action_name : String) -> void:
|
||||||
|
assigned_input_events[InputEventHelper.get_text(input_event)] = action_name
|
||||||
|
|
||||||
|
func _assign_input_event_to_action_group(input_event : InputEvent, action_name : String, action_group : int) -> void:
|
||||||
|
_assign_input_event(input_event, action_name)
|
||||||
|
var action_events := InputMap.action_get_events(action_name)
|
||||||
|
action_events.resize(action_events.size() + 1)
|
||||||
|
action_events[action_group] = input_event
|
||||||
|
InputMap.action_erase_events(action_name)
|
||||||
|
var final_action_events : Array[InputEvent]
|
||||||
|
for input_action_event in action_events:
|
||||||
|
if input_action_event == null: continue
|
||||||
|
final_action_events.append(input_action_event)
|
||||||
|
InputMap.action_add_event(action_name, input_action_event)
|
||||||
|
AppSettings.set_config_input_events(action_name, final_action_events)
|
||||||
|
action_group = min(action_group, final_action_events.size() - 1)
|
||||||
|
_update_assigned_inputs_and_button(action_name, action_group, input_event)
|
||||||
|
_update_next_button_disabled_state(action_name, action_group)
|
||||||
|
|
||||||
|
func _build_assigned_input_events() -> void:
|
||||||
|
assigned_input_events.clear()
|
||||||
|
var action_names := _get_all_action_names(show_built_in_actions and catch_built_in_duplicate_inputs)
|
||||||
|
for action_name in action_names:
|
||||||
|
var input_events = InputMap.action_get_events(action_name)
|
||||||
|
for input_event in input_events:
|
||||||
|
_assign_input_event(input_event, action_name)
|
||||||
|
|
||||||
|
func _get_action_for_input_event(input_event : InputEvent) -> String:
|
||||||
|
if InputEventHelper.get_text(input_event) in assigned_input_events:
|
||||||
|
return assigned_input_events[InputEventHelper.get_text(input_event)]
|
||||||
|
return ""
|
||||||
|
|
||||||
|
func add_action_event(last_input_text : String, last_input_event : InputEvent) -> void:
|
||||||
|
last_input_readable_name = last_input_text
|
||||||
|
if last_input_event != null:
|
||||||
|
var assigned_action := _get_action_for_input_event(last_input_event)
|
||||||
|
if not assigned_action.is_empty():
|
||||||
|
var readable_action_name = tr(_get_action_readable_name(assigned_action))
|
||||||
|
already_assigned.emit(readable_action_name, last_input_readable_name)
|
||||||
|
else:
|
||||||
|
_assign_input_event_to_action_group(last_input_event, editing_action_name, editing_action_group)
|
||||||
|
editing_action_name = ""
|
||||||
|
|
||||||
|
func _refresh_ui_list_button_content() -> void:
|
||||||
|
var action_names : Array[StringName] = _get_all_action_names(show_built_in_actions)
|
||||||
|
for action_name in action_names:
|
||||||
|
var input_events := InputMap.action_get_events(action_name)
|
||||||
|
if input_events.size() < 1:
|
||||||
|
continue
|
||||||
|
var group_iter : int = 0
|
||||||
|
for input_event in input_events:
|
||||||
|
_update_assigned_inputs_and_button(action_name, group_iter, input_event)
|
||||||
|
group_iter += 1
|
||||||
|
while group_iter < action_groups:
|
||||||
|
_clear_button(action_name, group_iter)
|
||||||
|
group_iter += 1
|
||||||
|
|
||||||
|
func reset() -> void:
|
||||||
|
AppSettings.reset_to_default_inputs()
|
||||||
|
_build_assigned_input_events()
|
||||||
|
_refresh_ui_list_button_content()
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
if Engine.is_editor_hint(): return
|
||||||
|
vertical = vertical
|
||||||
|
_build_assigned_input_events()
|
||||||
|
_build_ui_list()
|
||||||
|
if input_icon_mapper:
|
||||||
|
input_icon_mapper.joypad_device_changed.connect(_refresh_ui_list_button_content)
|
@ -0,0 +1 @@
|
|||||||
|
uid://b3q5fgjev8gyo
|
@ -0,0 +1,45 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://bxp45814v6ydv"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://b3q5fgjev8gyo" path="res://addons/maaacks_game_template/base/scenes/menus/options_menu/input/input_actions_list.gd" id="1_cxorh"]
|
||||||
|
|
||||||
|
[node name="InputActionsList" type="ScrollContainer"]
|
||||||
|
custom_minimum_size = Vector2(560, 240)
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
follow_focus = true
|
||||||
|
script = ExtResource("1_cxorh")
|
||||||
|
action_groups = 3
|
||||||
|
action_group_names = Array[String](["Primary", "Secondary", "Tertiary", "Quaternary", "Quinary"])
|
||||||
|
input_action_names = Array[StringName]([&"move_forward", &"move_backward", &"move_up", &"move_down", &"move_left", &"move_right", &"interact"])
|
||||||
|
readable_action_names = Array[String](["Move Forward", "Move Backward", "Move Up", "Move Down", "Move Left", "Move Right", "Interact"])
|
||||||
|
action_name_map = {
|
||||||
|
"interact": "Interact",
|
||||||
|
"move_backward": "Move Backward",
|
||||||
|
"move_down": "Move Down",
|
||||||
|
"move_forward": "Move Forward",
|
||||||
|
"move_left": "Move Left",
|
||||||
|
"move_right": "Move Right",
|
||||||
|
"move_up": "Move Up"
|
||||||
|
}
|
||||||
|
|
||||||
|
[node name="ParentBoxContainer" type="BoxContainer" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
vertical = true
|
||||||
|
|
||||||
|
[node name="ActionBoxContainer" type="BoxContainer" parent="ParentBoxContainer"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
visible = false
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
|
||||||
|
[node name="ActionNameLabel" type="Label" parent="ParentBoxContainer/ActionBoxContainer"]
|
||||||
|
custom_minimum_size = Vector2(150, 0)
|
||||||
|
layout_mode = 2
|
@ -0,0 +1,215 @@
|
|||||||
|
class_name InputActionsTree
|
||||||
|
extends Tree
|
||||||
|
|
||||||
|
signal already_assigned(action_name : String, input_name : String)
|
||||||
|
signal minimum_reached(action_name : String)
|
||||||
|
signal add_button_clicked(action_name : String)
|
||||||
|
signal remove_button_clicked(action_name : String, input_name : String)
|
||||||
|
|
||||||
|
@export var input_action_names : Array[StringName] :
|
||||||
|
set(value):
|
||||||
|
var _value_changed = input_action_names != value
|
||||||
|
input_action_names = value
|
||||||
|
if _value_changed:
|
||||||
|
var _new_readable_action_names : Array[String]
|
||||||
|
for action in input_action_names:
|
||||||
|
_new_readable_action_names.append(action.capitalize())
|
||||||
|
readable_action_names = _new_readable_action_names
|
||||||
|
|
||||||
|
@export var readable_action_names : Array[String] :
|
||||||
|
set(value):
|
||||||
|
var _value_changed = readable_action_names != value
|
||||||
|
readable_action_names = value
|
||||||
|
if _value_changed:
|
||||||
|
var _new_action_name_map : Dictionary
|
||||||
|
for iter in range(input_action_names.size()):
|
||||||
|
var _input_name : StringName = input_action_names[iter]
|
||||||
|
var _readable_name : String = readable_action_names[iter]
|
||||||
|
_new_action_name_map[_input_name] = _readable_name
|
||||||
|
action_name_map = _new_action_name_map
|
||||||
|
|
||||||
|
## Show action names that are not explicitely listed in an action name map.
|
||||||
|
@export var show_all_actions : bool = true
|
||||||
|
@export_group("Icons")
|
||||||
|
@export var add_button_texture : Texture2D
|
||||||
|
@export var remove_button_texture : Texture2D
|
||||||
|
@export var input_icon_mapper : InputIconMapper
|
||||||
|
@export_group("Built-in Actions")
|
||||||
|
## Shows Godot's built-in actions (action names starting with "ui_") in the tree.
|
||||||
|
@export var show_built_in_actions : bool = false
|
||||||
|
## Prevents assigning inputs that are already assigned to Godot's built-in actions (action names starting with "ui_"). Not recommended.
|
||||||
|
@export var catch_built_in_duplicate_inputs : bool = false
|
||||||
|
## Maps the names of built-in input actions to readable names for users.
|
||||||
|
@export var built_in_action_name_map := InputEventHelper.BUILT_IN_ACTION_NAME_MAP
|
||||||
|
@export_group("Debug")
|
||||||
|
## Maps the names of input actions to readable names for users.
|
||||||
|
@export var action_name_map : Dictionary
|
||||||
|
|
||||||
|
var tree_item_add_map : Dictionary = {}
|
||||||
|
var tree_item_remove_map : Dictionary = {}
|
||||||
|
var tree_item_action_map : Dictionary = {}
|
||||||
|
var assigned_input_events : Dictionary = {}
|
||||||
|
var editing_action_name : String = ""
|
||||||
|
var editing_item
|
||||||
|
var last_input_readable_name
|
||||||
|
|
||||||
|
func _start_tree() -> void:
|
||||||
|
clear()
|
||||||
|
create_item()
|
||||||
|
|
||||||
|
func _add_input_event_as_tree_item(action_name : String, input_event : InputEvent, parent_item : TreeItem) -> void:
|
||||||
|
var input_tree_item : TreeItem = create_item(parent_item)
|
||||||
|
var icon : Texture
|
||||||
|
if input_icon_mapper:
|
||||||
|
icon = input_icon_mapper.get_icon(input_event)
|
||||||
|
if icon:
|
||||||
|
input_tree_item.set_icon(0, icon)
|
||||||
|
input_tree_item.set_text(0, InputEventHelper.get_text(input_event))
|
||||||
|
if remove_button_texture != null:
|
||||||
|
input_tree_item.add_button(0, remove_button_texture, -1, false, "Remove")
|
||||||
|
tree_item_remove_map[input_tree_item] = input_event
|
||||||
|
tree_item_action_map[input_tree_item] = action_name
|
||||||
|
|
||||||
|
func _add_action_as_tree_item(readable_name : String, action_name : String, input_events : Array[InputEvent]) -> void:
|
||||||
|
var root_tree_item : TreeItem = get_root()
|
||||||
|
var action_tree_item : TreeItem = create_item(root_tree_item)
|
||||||
|
action_tree_item.set_text(0, readable_name)
|
||||||
|
tree_item_add_map[action_tree_item] = action_name
|
||||||
|
if add_button_texture != null:
|
||||||
|
action_tree_item.add_button(0, add_button_texture, -1, false, "Add")
|
||||||
|
for input_event in input_events:
|
||||||
|
_add_input_event_as_tree_item(action_name, input_event, action_tree_item)
|
||||||
|
|
||||||
|
func _get_all_action_names(include_built_in : bool = false) -> Array[StringName]:
|
||||||
|
var action_names : Array[StringName] = input_action_names.duplicate()
|
||||||
|
var full_action_name_map = action_name_map.duplicate()
|
||||||
|
if include_built_in:
|
||||||
|
for action_name in built_in_action_name_map:
|
||||||
|
if action_name is String:
|
||||||
|
action_name = StringName(action_name)
|
||||||
|
if action_name is StringName:
|
||||||
|
action_names.append(action_name)
|
||||||
|
if show_all_actions:
|
||||||
|
var all_actions := AppSettings.get_action_names(include_built_in)
|
||||||
|
for action_name in all_actions:
|
||||||
|
if not action_name in action_names:
|
||||||
|
action_names.append(action_name)
|
||||||
|
return action_names
|
||||||
|
|
||||||
|
func _get_action_readable_name(input_name : StringName) -> String:
|
||||||
|
var readable_name : String
|
||||||
|
if input_name in action_name_map:
|
||||||
|
readable_name = action_name_map[input_name]
|
||||||
|
elif input_name in built_in_action_name_map:
|
||||||
|
readable_name = built_in_action_name_map[input_name]
|
||||||
|
else:
|
||||||
|
readable_name = input_name.capitalize()
|
||||||
|
action_name_map[input_name] = readable_name
|
||||||
|
return readable_name
|
||||||
|
|
||||||
|
func _build_ui_tree() -> void:
|
||||||
|
_start_tree()
|
||||||
|
var action_names : Array[StringName] = _get_all_action_names(show_built_in_actions)
|
||||||
|
for action_name in action_names:
|
||||||
|
var input_events = InputMap.action_get_events(action_name)
|
||||||
|
if input_events.size() < 1:
|
||||||
|
continue
|
||||||
|
var readable_name : String = _get_action_readable_name(action_name)
|
||||||
|
_add_action_as_tree_item(readable_name, action_name, input_events)
|
||||||
|
|
||||||
|
func _assign_input_event(input_event : InputEvent, action_name : String) -> void:
|
||||||
|
assigned_input_events[InputEventHelper.get_text(input_event)] = action_name
|
||||||
|
|
||||||
|
func _assign_input_event_to_action(input_event : InputEvent, action_name : String) -> void:
|
||||||
|
_assign_input_event(input_event, action_name)
|
||||||
|
InputMap.action_add_event(action_name, input_event)
|
||||||
|
var action_events = InputMap.action_get_events(action_name)
|
||||||
|
AppSettings.set_config_input_events(action_name, action_events)
|
||||||
|
_add_input_event_as_tree_item(action_name, input_event, editing_item)
|
||||||
|
|
||||||
|
func _can_remove_input_event(action_name : String) -> bool:
|
||||||
|
return InputMap.action_get_events(action_name).size() > 1
|
||||||
|
|
||||||
|
func _remove_input_event(input_event : InputEvent) -> void:
|
||||||
|
assigned_input_events.erase(InputEventHelper.get_text(input_event))
|
||||||
|
|
||||||
|
func _remove_input_event_from_action(input_event : InputEvent, action_name : String) -> void:
|
||||||
|
_remove_input_event(input_event)
|
||||||
|
AppSettings.remove_action_input_event(action_name, input_event)
|
||||||
|
|
||||||
|
func _build_assigned_input_events() -> void:
|
||||||
|
assigned_input_events.clear()
|
||||||
|
var action_names := _get_all_action_names(show_built_in_actions and catch_built_in_duplicate_inputs)
|
||||||
|
for action_name in action_names:
|
||||||
|
var input_events = InputMap.action_get_events(action_name)
|
||||||
|
for input_event in input_events:
|
||||||
|
_assign_input_event(input_event, action_name)
|
||||||
|
|
||||||
|
func _get_action_for_input_event(input_event : InputEvent) -> String:
|
||||||
|
if InputEventHelper.get_text(input_event) in assigned_input_events:
|
||||||
|
return assigned_input_events[InputEventHelper.get_text(input_event)]
|
||||||
|
return ""
|
||||||
|
|
||||||
|
func add_action_event(last_input_text : String, last_input_event : InputEvent):
|
||||||
|
last_input_readable_name = last_input_text
|
||||||
|
if last_input_event != null:
|
||||||
|
var assigned_action := _get_action_for_input_event(last_input_event)
|
||||||
|
if not assigned_action.is_empty():
|
||||||
|
var readable_action_name = tr(_get_action_readable_name(assigned_action))
|
||||||
|
already_assigned.emit(readable_action_name, last_input_readable_name)
|
||||||
|
else:
|
||||||
|
_assign_input_event_to_action(last_input_event, editing_action_name)
|
||||||
|
editing_action_name = ""
|
||||||
|
|
||||||
|
func remove_action_event(item : TreeItem) -> void:
|
||||||
|
if item not in tree_item_remove_map:
|
||||||
|
return
|
||||||
|
var action_name = tree_item_action_map[item]
|
||||||
|
var input_event = tree_item_remove_map[item]
|
||||||
|
if not _can_remove_input_event(action_name):
|
||||||
|
var readable_action_name = _get_action_readable_name(action_name)
|
||||||
|
minimum_reached.emit(readable_action_name)
|
||||||
|
return
|
||||||
|
_remove_input_event_from_action(input_event, action_name)
|
||||||
|
var parent_tree_item = item.get_parent()
|
||||||
|
parent_tree_item.remove_child(item)
|
||||||
|
|
||||||
|
func reset() -> void:
|
||||||
|
AppSettings.reset_to_default_inputs()
|
||||||
|
_build_assigned_input_events()
|
||||||
|
_build_ui_tree()
|
||||||
|
|
||||||
|
func _add_item(item : TreeItem) -> void:
|
||||||
|
editing_item = item
|
||||||
|
editing_action_name = tree_item_add_map[item]
|
||||||
|
var readable_action_name = tr(_get_action_readable_name(editing_action_name))
|
||||||
|
add_button_clicked.emit(readable_action_name)
|
||||||
|
|
||||||
|
func _remove_item(item : TreeItem) -> void:
|
||||||
|
editing_item = item
|
||||||
|
editing_action_name = tree_item_action_map[item]
|
||||||
|
var readable_action_name = tr(_get_action_readable_name(editing_action_name))
|
||||||
|
var item_text = item.get_text(0)
|
||||||
|
remove_button_clicked.emit(readable_action_name, item_text)
|
||||||
|
|
||||||
|
func _check_item_actions(item : TreeItem) -> void:
|
||||||
|
if item in tree_item_add_map:
|
||||||
|
_add_item(item)
|
||||||
|
elif item in tree_item_remove_map:
|
||||||
|
_remove_item(item)
|
||||||
|
|
||||||
|
func _on_button_clicked(item : TreeItem, _column, _id, _mouse_button_index) -> void:
|
||||||
|
_check_item_actions(item)
|
||||||
|
|
||||||
|
func _on_item_activated() -> void:
|
||||||
|
var item = get_selected()
|
||||||
|
_check_item_actions(item)
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
if Engine.is_editor_hint(): return
|
||||||
|
_build_assigned_input_events()
|
||||||
|
_build_ui_tree()
|
||||||
|
button_clicked.connect(_on_button_clicked)
|
||||||
|
item_activated.connect(_on_item_activated)
|
||||||
|
if input_icon_mapper:
|
||||||
|
input_icon_mapper.joypad_device_changed.connect(_build_ui_tree)
|
@ -0,0 +1 @@
|
|||||||
|
uid://bp7d2e5djo2tp
|
@ -0,0 +1,24 @@
|
|||||||
|
[gd_scene load_steps=4 format=3 uid="uid://ci6wgl2ngd35n"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://bp7d2e5djo2tp" path="res://addons/maaacks_game_template/base/scenes/menus/options_menu/input/input_actions_tree.gd" id="1_o33o4"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://c1eqf1cse1hch" path="res://addons/maaacks_game_template/base/assets/remapping_input_icons/addition_symbol.png" id="2_ppi0j"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://bteq3ica74h30" path="res://addons/maaacks_game_template/base/assets/remapping_input_icons/subtraction_symbol.png" id="3_hb3xh"]
|
||||||
|
|
||||||
|
[node name="InputActionsTree" type="Tree"]
|
||||||
|
custom_minimum_size = Vector2(400, 240)
|
||||||
|
size_flags_vertical = 3
|
||||||
|
hide_root = true
|
||||||
|
script = ExtResource("1_o33o4")
|
||||||
|
input_action_names = Array[StringName]([&"move_forward", &"move_backward", &"move_up", &"move_down", &"move_left", &"move_right", &"interact"])
|
||||||
|
readable_action_names = Array[String](["Move Forward", "Move Backward", "Move Up", "Move Down", "Move Left", "Move Right", "Interact"])
|
||||||
|
add_button_texture = ExtResource("2_ppi0j")
|
||||||
|
remove_button_texture = ExtResource("3_hb3xh")
|
||||||
|
action_name_map = {
|
||||||
|
"interact": "Interact",
|
||||||
|
"move_backward": "Move Backward",
|
||||||
|
"move_down": "Move Down",
|
||||||
|
"move_forward": "Move Forward",
|
||||||
|
"move_left": "Move Left",
|
||||||
|
"move_right": "Move Right",
|
||||||
|
"move_up": "Move Up"
|
||||||
|
}
|
@ -0,0 +1,140 @@
|
|||||||
|
@tool
|
||||||
|
class_name InputIconMapper
|
||||||
|
extends FileLister
|
||||||
|
|
||||||
|
signal joypad_device_changed
|
||||||
|
|
||||||
|
const COMMON_REPLACE_STRINGS: Dictionary = {
|
||||||
|
"L 1": "Left Shoulder",
|
||||||
|
"R 1": "Right Shoulder",
|
||||||
|
"L 2": "Left Trigger",
|
||||||
|
"R 2": "Right Trigger",
|
||||||
|
"Lt": "Left Trigger",
|
||||||
|
"Rt": "Right Trigger",
|
||||||
|
"Lb": "Left Shoulder",
|
||||||
|
"Rb": "Right Shoulder",
|
||||||
|
} # Dictionary[String, String]
|
||||||
|
## Gives priority to icons with occurrences of the provided strings.
|
||||||
|
@export var prioritized_strings : Array[String]
|
||||||
|
## Replaces the first occurence in icon names of the key with the value.
|
||||||
|
@export var replace_strings : Dictionary # Dictionary[String, String]
|
||||||
|
## Filters the icon names of the provided strings.
|
||||||
|
@export var filtered_strings : Array[String]
|
||||||
|
## Adds entries for "Up", "Down", "Left", "Right" to icon names ending with "Stick".
|
||||||
|
@export var add_stick_directions : bool = false
|
||||||
|
@export var intial_joypad_device : String = InputEventHelper.DEVICE_GENERIC
|
||||||
|
## Attempt to match the icon names to the input names based on the string rules.
|
||||||
|
@export var _match_icons_to_inputs_action : bool = false :
|
||||||
|
set(value):
|
||||||
|
if value and Engine.is_editor_hint():
|
||||||
|
_match_icons_to_inputs()
|
||||||
|
# For Godot 4.4
|
||||||
|
# @export_tool_button("Match Icons to Inputs") var _match_icons_to_inputs_action = _match_icons_to_inputs
|
||||||
|
@export var matching_icons : Dictionary # Dictionary[String, Texture]
|
||||||
|
@export_group("Debug")
|
||||||
|
@export var all_icons : Dictionary # Dictionary[String, Texture]
|
||||||
|
|
||||||
|
@onready var last_joypad_device = intial_joypad_device
|
||||||
|
|
||||||
|
func _is_end_of_word(full_string : String, what : String) -> bool:
|
||||||
|
var string_end_position = full_string.find(what) + what.length()
|
||||||
|
var end_of_word : bool
|
||||||
|
if string_end_position + 1 < full_string.length():
|
||||||
|
var next_character = full_string.substr(string_end_position, 1)
|
||||||
|
end_of_word = next_character == " "
|
||||||
|
return full_string.ends_with(what) or end_of_word
|
||||||
|
|
||||||
|
func _get_standard_joy_name(joy_name : String) -> String:
|
||||||
|
var all_replace_strings := replace_strings.duplicate()
|
||||||
|
all_replace_strings.merge(COMMON_REPLACE_STRINGS)
|
||||||
|
for what in all_replace_strings:
|
||||||
|
if joy_name.contains(what) and _is_end_of_word(joy_name, what):
|
||||||
|
var position = joy_name.find(what)
|
||||||
|
joy_name = joy_name.erase(position, what.length())
|
||||||
|
joy_name = joy_name.insert(position, all_replace_strings[what])
|
||||||
|
var combined_joystick_name : Array[String] = []
|
||||||
|
for part in joy_name.split(" "):
|
||||||
|
if part.to_lower() in filtered_strings:
|
||||||
|
continue
|
||||||
|
if not part.is_empty():
|
||||||
|
combined_joystick_name.append(part)
|
||||||
|
joy_name = " ".join(combined_joystick_name)
|
||||||
|
joy_name = joy_name.strip_edges()
|
||||||
|
return joy_name
|
||||||
|
|
||||||
|
func _match_icon_to_file(file : String) -> void:
|
||||||
|
var matching_string : String = file.get_file().get_basename()
|
||||||
|
var icon : Texture = load(file)
|
||||||
|
if not icon:
|
||||||
|
return
|
||||||
|
all_icons[matching_string] = icon
|
||||||
|
matching_string = matching_string.capitalize()
|
||||||
|
matching_string = _get_standard_joy_name(matching_string)
|
||||||
|
matching_string = matching_string.strip_edges()
|
||||||
|
if add_stick_directions and matching_string.ends_with("Stick"):
|
||||||
|
matching_icons[matching_string + " Up"] = icon
|
||||||
|
matching_icons[matching_string + " Down"] = icon
|
||||||
|
matching_icons[matching_string + " Left"] = icon
|
||||||
|
matching_icons[matching_string + " Right"] = icon
|
||||||
|
return
|
||||||
|
if matching_string in matching_icons:
|
||||||
|
return
|
||||||
|
matching_icons[matching_string] = icon
|
||||||
|
|
||||||
|
func _prioritized_files() -> Array[String]:
|
||||||
|
var priority_levels : Dictionary # Dictionary[String, int]
|
||||||
|
var priortized_files : Array[String]
|
||||||
|
for prioritized_string in prioritized_strings:
|
||||||
|
for file in files:
|
||||||
|
if file.containsn(prioritized_string):
|
||||||
|
if file in priority_levels:
|
||||||
|
priority_levels[file] += 1
|
||||||
|
else:
|
||||||
|
priority_levels[file] = 1
|
||||||
|
var priority_file_map : Dictionary # Dictionary[int, Array]
|
||||||
|
var max_priority_level : int = 0
|
||||||
|
for file in priority_levels:
|
||||||
|
var priority_level = priority_levels[file]
|
||||||
|
max_priority_level = max(priority_level, max_priority_level)
|
||||||
|
if priority_level in priority_file_map:
|
||||||
|
priority_file_map[priority_level].append(file)
|
||||||
|
else:
|
||||||
|
priority_file_map[priority_level] = [file]
|
||||||
|
while max_priority_level > 0:
|
||||||
|
for priority_file in priority_file_map[max_priority_level]:
|
||||||
|
priortized_files.append(priority_file)
|
||||||
|
max_priority_level -= 1
|
||||||
|
return priortized_files
|
||||||
|
|
||||||
|
func _match_icons_to_inputs() -> void:
|
||||||
|
matching_icons.clear()
|
||||||
|
all_icons.clear()
|
||||||
|
for prioritized_file in _prioritized_files():
|
||||||
|
_match_icon_to_file(prioritized_file)
|
||||||
|
for file in files:
|
||||||
|
_match_icon_to_file(file)
|
||||||
|
|
||||||
|
func get_icon(input_event : InputEvent) -> Texture:
|
||||||
|
var specific_text = InputEventHelper.get_device_specific_text(input_event, last_joypad_device)
|
||||||
|
if specific_text in matching_icons:
|
||||||
|
return matching_icons[specific_text]
|
||||||
|
return null
|
||||||
|
|
||||||
|
func _assign_joypad_0_to_last() -> void:
|
||||||
|
if last_joypad_device != intial_joypad_device : return
|
||||||
|
var connected_joypads := Input.get_connected_joypads()
|
||||||
|
if connected_joypads.is_empty(): return
|
||||||
|
last_joypad_device = InputEventHelper.get_device_name_by_id(connected_joypads[0])
|
||||||
|
|
||||||
|
func _input(event : InputEvent) -> void:
|
||||||
|
var device_name = InputEventHelper.get_device_name(event)
|
||||||
|
if device_name != InputEventHelper.DEVICE_GENERIC and device_name != last_joypad_device:
|
||||||
|
last_joypad_device = device_name
|
||||||
|
joypad_device_changed.emit()
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
_assign_joypad_0_to_last()
|
||||||
|
if files.size() == 0:
|
||||||
|
_refresh_files()
|
||||||
|
if matching_icons.size() == 0:
|
||||||
|
_match_icons_to_inputs()
|
@ -0,0 +1 @@
|
|||||||
|
uid://cqigj1uumknrp
|
@ -0,0 +1,6 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://qoexj4ptqt8a"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://cqigj1uumknrp" path="res://addons/maaacks_game_template/base/scenes/menus/options_menu/input/input_icon_mapper.gd" id="1_msrpt"]
|
||||||
|
|
||||||
|
[node name="InputIconMapper" type="Node"]
|
||||||
|
script = ExtResource("1_msrpt")
|
@ -0,0 +1,102 @@
|
|||||||
|
@tool
|
||||||
|
class_name InputOptionsMenu
|
||||||
|
extends Control
|
||||||
|
|
||||||
|
const ALREADY_ASSIGNED_TEXT : String = "{key} already assigned to {action}."
|
||||||
|
const ONE_INPUT_MINIMUM_TEXT : String = "%s must have at least one key or button assigned."
|
||||||
|
const KEY_DELETION_TEXT : String = "Are you sure you want to remove {key} from {action}?"
|
||||||
|
|
||||||
|
@export_enum("List", "Tree") var remapping_mode : int = 0 :
|
||||||
|
set(value):
|
||||||
|
remapping_mode = value
|
||||||
|
if is_inside_tree():
|
||||||
|
match(remapping_mode):
|
||||||
|
0:
|
||||||
|
%InputActionsList.show()
|
||||||
|
%InputActionsTree.hide()
|
||||||
|
1:
|
||||||
|
%InputActionsList.hide()
|
||||||
|
%InputActionsTree.show()
|
||||||
|
|
||||||
|
@onready var assignment_placeholder_text = $KeyAssignmentDialog.dialog_text
|
||||||
|
|
||||||
|
var last_input_readable_name
|
||||||
|
|
||||||
|
func _horizontally_align_popup_labels() -> void:
|
||||||
|
$KeyAssignmentDialog.get_label().horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
$KeyDeletionDialog.get_label().horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
$OneInputMinimumDialog.get_label().horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
$AlreadyAssignedDialog.get_label().horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
$ResetConfirmationDialog.get_label().horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
remapping_mode = remapping_mode
|
||||||
|
if Engine.is_editor_hint(): return
|
||||||
|
_horizontally_align_popup_labels()
|
||||||
|
|
||||||
|
func _add_action_event() -> void:
|
||||||
|
var last_input_event = $KeyAssignmentDialog.last_input_event
|
||||||
|
last_input_readable_name = $KeyAssignmentDialog.last_input_text
|
||||||
|
match(remapping_mode):
|
||||||
|
0:
|
||||||
|
%InputActionsList.add_action_event(last_input_readable_name, last_input_event)
|
||||||
|
1:
|
||||||
|
%InputActionsTree.add_action_event(last_input_readable_name, last_input_event)
|
||||||
|
|
||||||
|
func _remove_action_event(item : TreeItem) -> void:
|
||||||
|
%InputActionsTree.remove_action_event(item)
|
||||||
|
|
||||||
|
func _on_reset_button_pressed() -> void:
|
||||||
|
$ResetConfirmationDialog.popup_centered()
|
||||||
|
|
||||||
|
func _on_key_deletion_dialog_confirmed() -> void:
|
||||||
|
var editing_item = %InputActionsTree.editing_item
|
||||||
|
if is_instance_valid(editing_item):
|
||||||
|
_remove_action_event(editing_item)
|
||||||
|
|
||||||
|
func _on_key_assignment_dialog_confirmed() -> void:
|
||||||
|
_add_action_event()
|
||||||
|
|
||||||
|
func _open_key_assignment_dialog(action_name : String, readable_input_name : String = assignment_placeholder_text) -> void:
|
||||||
|
$KeyAssignmentDialog.title = tr("Assign Key for {action}").format({action = action_name})
|
||||||
|
$KeyAssignmentDialog.dialog_text = readable_input_name
|
||||||
|
$KeyAssignmentDialog.get_ok_button().disabled = true
|
||||||
|
$KeyAssignmentDialog.popup_centered()
|
||||||
|
|
||||||
|
func _on_input_actions_tree_add_button_clicked(action_name) -> void:
|
||||||
|
_open_key_assignment_dialog(action_name)
|
||||||
|
|
||||||
|
func _on_input_actions_tree_remove_button_clicked(action_name, input_name) -> void:
|
||||||
|
$KeyDeletionDialog.title = tr("Remove Key for {action}").format({action = action_name})
|
||||||
|
$KeyDeletionDialog.dialog_text = tr(KEY_DELETION_TEXT).format({key = input_name, action = action_name})
|
||||||
|
$KeyDeletionDialog.popup_centered()
|
||||||
|
|
||||||
|
func _popup_already_assigned(action_name, input_name) -> void:
|
||||||
|
$AlreadyAssignedDialog.dialog_text = tr(ALREADY_ASSIGNED_TEXT).format({key = input_name, action = action_name})
|
||||||
|
$AlreadyAssignedDialog.popup_centered.call_deferred()
|
||||||
|
|
||||||
|
func _popup_minimum_reached(action_name : String) -> void:
|
||||||
|
$OneInputMinimumDialog.dialog_text = ONE_INPUT_MINIMUM_TEXT % action_name
|
||||||
|
$OneInputMinimumDialog.popup_centered.call_deferred()
|
||||||
|
|
||||||
|
func _on_input_actions_tree_already_assigned(action_name, input_name) -> void:
|
||||||
|
_popup_already_assigned(action_name, input_name)
|
||||||
|
|
||||||
|
func _on_input_actions_tree_minimum_reached(action_name) -> void:
|
||||||
|
_popup_minimum_reached(action_name)
|
||||||
|
|
||||||
|
func _on_input_actions_list_already_assigned(action_name, input_name) -> void:
|
||||||
|
_popup_already_assigned(action_name, input_name)
|
||||||
|
|
||||||
|
func _on_input_actions_list_minimum_reached(action_name) -> void:
|
||||||
|
_popup_minimum_reached(action_name)
|
||||||
|
|
||||||
|
func _on_input_actions_list_button_clicked(action_name, readable_input_name) -> void:
|
||||||
|
_open_key_assignment_dialog(action_name, readable_input_name)
|
||||||
|
|
||||||
|
func _on_reset_confirmation_dialog_confirmed() -> void:
|
||||||
|
match(remapping_mode):
|
||||||
|
0:
|
||||||
|
%InputActionsList.reset()
|
||||||
|
1:
|
||||||
|
%InputActionsTree.reset()
|
@ -0,0 +1 @@
|
|||||||
|
uid://eborw7q4b07h
|
@ -0,0 +1,136 @@
|
|||||||
|
[gd_scene load_steps=7 format=3 uid="uid://dp3rgqaehb3xu"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://eborw7q4b07h" path="res://addons/maaacks_game_template/base/scenes/menus/options_menu/input/input_options_menu.gd" id="1"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://qoexj4ptqt8a" path="res://addons/maaacks_game_template/base/scenes/menus/options_menu/input/input_icon_mapper.tscn" id="2_627ul"]
|
||||||
|
[ext_resource type="Script" uid="uid://1nf36h0gms3q" path="res://addons/maaacks_game_template/base/scripts/capture_focus.gd" id="2_wft4x"]
|
||||||
|
[ext_resource type="Script" uid="uid://custha7r0uoic" path="res://addons/maaacks_game_template/base/scenes/menus/options_menu/input/key_assignment_dialog.gd" id="3_wsh2h"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://bxp45814v6ydv" path="res://addons/maaacks_game_template/base/scenes/menus/options_menu/input/input_actions_list.tscn" id="4_lf2nw"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://ci6wgl2ngd35n" path="res://addons/maaacks_game_template/base/scenes/menus/options_menu/input/input_actions_tree.tscn" id="5_b2whh"]
|
||||||
|
|
||||||
|
[node name="Controls" type="MarginContainer"]
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
theme_override_constants/margin_left = 32
|
||||||
|
theme_override_constants/margin_top = 8
|
||||||
|
theme_override_constants/margin_right = 32
|
||||||
|
theme_override_constants/margin_bottom = 8
|
||||||
|
script = ExtResource("1")
|
||||||
|
|
||||||
|
[node name="InputIconMapper" parent="." instance=ExtResource("2_627ul")]
|
||||||
|
|
||||||
|
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 4
|
||||||
|
size_flags_vertical = 4
|
||||||
|
script = ExtResource("2_wft4x")
|
||||||
|
search_depth = 5
|
||||||
|
|
||||||
|
[node name="InputMappingContainer" type="VBoxContainer" parent="VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
|
||||||
|
[node name="Label" type="Label" parent="VBoxContainer/InputMappingContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Actions & Inputs"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="InputActionsList" parent="VBoxContainer/InputMappingContainer" node_paths=PackedStringArray("input_icon_mapper") instance=ExtResource("4_lf2nw")]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
input_icon_mapper = NodePath("../../../InputIconMapper")
|
||||||
|
|
||||||
|
[node name="InputActionsTree" parent="VBoxContainer/InputMappingContainer" node_paths=PackedStringArray("input_icon_mapper") instance=ExtResource("5_b2whh")]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
visible = false
|
||||||
|
layout_mode = 2
|
||||||
|
input_icon_mapper = NodePath("../../../InputIconMapper")
|
||||||
|
|
||||||
|
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/InputMappingContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="ResetButton" type="Button" parent="VBoxContainer/InputMappingContainer/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Reset"
|
||||||
|
|
||||||
|
[node name="KeyAssignmentDialog" type="ConfirmationDialog" parent="."]
|
||||||
|
title = "Assign Key"
|
||||||
|
size = Vector2i(400, 158)
|
||||||
|
dialog_text = "
|
||||||
|
|
||||||
|
|
||||||
|
"
|
||||||
|
script = ExtResource("3_wsh2h")
|
||||||
|
|
||||||
|
[node name="VBoxContainer" type="VBoxContainer" parent="KeyAssignmentDialog"]
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_left = 8.0
|
||||||
|
offset_top = 8.0
|
||||||
|
offset_right = -8.0
|
||||||
|
offset_bottom = -49.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
|
||||||
|
[node name="InputLabel" type="Label" parent="KeyAssignmentDialog/VBoxContainer"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
text = "None"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="InputTextEdit" type="TextEdit" parent="KeyAssignmentDialog/VBoxContainer"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
placeholder_text = "Focus here to assign inputs."
|
||||||
|
context_menu_enabled = false
|
||||||
|
shortcut_keys_enabled = false
|
||||||
|
selecting_enabled = false
|
||||||
|
deselect_on_focus_loss_enabled = false
|
||||||
|
drag_and_drop_selection_enabled = false
|
||||||
|
middle_mouse_paste_enabled = false
|
||||||
|
caret_move_on_right_click = false
|
||||||
|
|
||||||
|
[node name="DelayTimer" type="Timer" parent="KeyAssignmentDialog"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
wait_time = 0.1
|
||||||
|
one_shot = true
|
||||||
|
|
||||||
|
[node name="KeyDeletionDialog" type="ConfirmationDialog" parent="."]
|
||||||
|
title = "Remove Key"
|
||||||
|
size = Vector2i(419, 100)
|
||||||
|
dialog_text = "Are you sure you want to remove KEY from ACTION?"
|
||||||
|
|
||||||
|
[node name="OneInputMinimumDialog" type="AcceptDialog" parent="."]
|
||||||
|
title = "Cannot Remove"
|
||||||
|
size = Vector2i(398, 100)
|
||||||
|
|
||||||
|
[node name="AlreadyAssignedDialog" type="AcceptDialog" parent="."]
|
||||||
|
title = "Already Assigned"
|
||||||
|
size = Vector2i(398, 100)
|
||||||
|
|
||||||
|
[node name="ResetConfirmationDialog" type="ConfirmationDialog" parent="."]
|
||||||
|
size = Vector2i(486, 100)
|
||||||
|
dialog_text = "Are you sure you want to reset controls back to the defaults?"
|
||||||
|
|
||||||
|
[connection signal="already_assigned" from="VBoxContainer/InputMappingContainer/InputActionsList" to="." method="_on_input_actions_list_already_assigned"]
|
||||||
|
[connection signal="button_clicked" from="VBoxContainer/InputMappingContainer/InputActionsList" to="." method="_on_input_actions_list_button_clicked"]
|
||||||
|
[connection signal="minimum_reached" from="VBoxContainer/InputMappingContainer/InputActionsList" to="." method="_on_input_actions_list_minimum_reached"]
|
||||||
|
[connection signal="add_button_clicked" from="VBoxContainer/InputMappingContainer/InputActionsTree" to="." method="_on_input_actions_tree_add_button_clicked"]
|
||||||
|
[connection signal="already_assigned" from="VBoxContainer/InputMappingContainer/InputActionsTree" to="." method="_on_input_actions_tree_already_assigned"]
|
||||||
|
[connection signal="minimum_reached" from="VBoxContainer/InputMappingContainer/InputActionsTree" to="." method="_on_input_actions_tree_minimum_reached"]
|
||||||
|
[connection signal="remove_button_clicked" from="VBoxContainer/InputMappingContainer/InputActionsTree" to="." method="_on_input_actions_tree_remove_button_clicked"]
|
||||||
|
[connection signal="pressed" from="VBoxContainer/InputMappingContainer/HBoxContainer/ResetButton" to="." method="_on_reset_button_pressed"]
|
||||||
|
[connection signal="confirmed" from="KeyAssignmentDialog" to="." method="_on_key_assignment_dialog_confirmed"]
|
||||||
|
[connection signal="visibility_changed" from="KeyAssignmentDialog" to="KeyAssignmentDialog" method="_on_visibility_changed"]
|
||||||
|
[connection signal="focus_entered" from="KeyAssignmentDialog/VBoxContainer/InputTextEdit" to="KeyAssignmentDialog" method="_on_text_edit_focus_entered"]
|
||||||
|
[connection signal="focus_exited" from="KeyAssignmentDialog/VBoxContainer/InputTextEdit" to="KeyAssignmentDialog" method="_on_input_text_edit_focus_exited"]
|
||||||
|
[connection signal="gui_input" from="KeyAssignmentDialog/VBoxContainer/InputTextEdit" to="KeyAssignmentDialog" method="_on_input_text_edit_gui_input"]
|
||||||
|
[connection signal="confirmed" from="KeyDeletionDialog" to="." method="_on_key_deletion_dialog_confirmed"]
|
||||||
|
[connection signal="confirmed" from="ResetConfirmationDialog" to="." method="_on_reset_confirmation_dialog_confirmed"]
|
@ -0,0 +1,104 @@
|
|||||||
|
extends ConfirmationDialog
|
||||||
|
|
||||||
|
const LISTENING_TEXT : String = "Listening for input..."
|
||||||
|
const FOCUS_HERE_TEXT : String = "Focus here to assign inputs."
|
||||||
|
const CONFIRM_INPUT_TEXT : String = "Press again to confirm..."
|
||||||
|
const NO_INPUT_TEXT : String = "None"
|
||||||
|
|
||||||
|
enum InputConfirmation {
|
||||||
|
SINGLE,
|
||||||
|
DOUBLE,
|
||||||
|
OK_BUTTON
|
||||||
|
}
|
||||||
|
@export var input_confirmation : InputConfirmation = InputConfirmation.SINGLE
|
||||||
|
|
||||||
|
var last_input_event : InputEvent
|
||||||
|
var last_input_text : String
|
||||||
|
var listening : bool = false
|
||||||
|
var confirming : bool = false
|
||||||
|
|
||||||
|
func _record_input_event(event : InputEvent) -> void:
|
||||||
|
last_input_text = InputEventHelper.get_text(event)
|
||||||
|
if last_input_text.is_empty():
|
||||||
|
return
|
||||||
|
last_input_event = event
|
||||||
|
%InputLabel.text = last_input_text
|
||||||
|
get_ok_button().disabled = false
|
||||||
|
|
||||||
|
func _is_recordable_input(event : InputEvent) -> bool:
|
||||||
|
return event != null and \
|
||||||
|
(event is InputEventKey or \
|
||||||
|
event is InputEventMouseButton or \
|
||||||
|
event is InputEventJoypadButton or \
|
||||||
|
(event is InputEventJoypadMotion and \
|
||||||
|
abs(event.axis_value) > 0.5)) and \
|
||||||
|
event.is_pressed()
|
||||||
|
|
||||||
|
func _start_listening() -> void:
|
||||||
|
%InputTextEdit.placeholder_text = LISTENING_TEXT
|
||||||
|
listening = true
|
||||||
|
%DelayTimer.start()
|
||||||
|
|
||||||
|
func _stop_listening() -> void:
|
||||||
|
%InputTextEdit.placeholder_text = FOCUS_HERE_TEXT
|
||||||
|
listening = false
|
||||||
|
confirming = false
|
||||||
|
|
||||||
|
func _on_text_edit_focus_entered() -> void:
|
||||||
|
_start_listening.call_deferred()
|
||||||
|
|
||||||
|
func _on_input_text_edit_focus_exited() -> void:
|
||||||
|
_stop_listening()
|
||||||
|
|
||||||
|
func _focus_on_ok() -> void:
|
||||||
|
get_ok_button().grab_focus()
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
get_ok_button().focus_neighbor_top = ^"../../%InputTextEdit"
|
||||||
|
get_cancel_button().focus_neighbor_top = ^"../../%InputTextEdit"
|
||||||
|
|
||||||
|
func _input_matches_last(event : InputEvent) -> bool:
|
||||||
|
return last_input_text == InputEventHelper.get_text(event)
|
||||||
|
|
||||||
|
func _is_mouse_input(event : InputEvent) -> bool:
|
||||||
|
return event is InputEventMouse
|
||||||
|
|
||||||
|
func _input_confirms_choice(event : InputEvent) -> bool:
|
||||||
|
return confirming and not _is_mouse_input(event) and _input_matches_last(event)
|
||||||
|
|
||||||
|
func _should_process_input_event(event : InputEvent) -> bool:
|
||||||
|
return listening and _is_recordable_input(event) and %DelayTimer.is_stopped()
|
||||||
|
|
||||||
|
func _should_confirm_input_event(event : InputEvent) -> bool:
|
||||||
|
return not _is_mouse_input(event)
|
||||||
|
|
||||||
|
func _confirm_choice() -> void:
|
||||||
|
confirmed.emit()
|
||||||
|
hide()
|
||||||
|
|
||||||
|
func _process_input_event(event : InputEvent) -> void:
|
||||||
|
if not _should_process_input_event(event):
|
||||||
|
return
|
||||||
|
if _input_confirms_choice(event):
|
||||||
|
confirming = false
|
||||||
|
if input_confirmation == InputConfirmation.DOUBLE:
|
||||||
|
_confirm_choice()
|
||||||
|
else:
|
||||||
|
_focus_on_ok.call_deferred()
|
||||||
|
return
|
||||||
|
_record_input_event(event)
|
||||||
|
if input_confirmation == InputConfirmation.SINGLE:
|
||||||
|
_confirm_choice()
|
||||||
|
if _should_confirm_input_event(event):
|
||||||
|
confirming = true
|
||||||
|
%DelayTimer.start()
|
||||||
|
%InputTextEdit.placeholder_text = CONFIRM_INPUT_TEXT
|
||||||
|
|
||||||
|
func _on_input_text_edit_gui_input(event) -> void:
|
||||||
|
%InputTextEdit.set_deferred("text", "")
|
||||||
|
_process_input_event(event)
|
||||||
|
|
||||||
|
func _on_visibility_changed() -> void:
|
||||||
|
if visible:
|
||||||
|
%InputLabel.text = NO_INPUT_TEXT
|
||||||
|
%InputTextEdit.grab_focus()
|
@ -0,0 +1 @@
|
|||||||
|
uid://custha7r0uoic
|
@ -0,0 +1,13 @@
|
|||||||
|
class_name MasterOptionsMenu
|
||||||
|
extends Control
|
||||||
|
|
||||||
|
func _unhandled_input(event : InputEvent) -> void:
|
||||||
|
if not is_visible_in_tree():
|
||||||
|
return
|
||||||
|
if event.is_action_pressed("ui_page_down"):
|
||||||
|
$TabContainer.current_tab = ($TabContainer.current_tab+1) % $TabContainer.get_tab_count()
|
||||||
|
elif event.is_action_pressed("ui_page_up"):
|
||||||
|
if $TabContainer.current_tab == 0:
|
||||||
|
$TabContainer.current_tab = $TabContainer.get_tab_count()-1
|
||||||
|
else:
|
||||||
|
$TabContainer.current_tab = $TabContainer.current_tab-1
|
@ -0,0 +1 @@
|
|||||||
|
uid://c3mignmhuvvq4
|
@ -0,0 +1,23 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://bvwl11s2p0hd"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://c3mignmhuvvq4" path="res://addons/maaacks_game_template/base/scenes/menus/options_menu/master_options_menu.gd" id="1_u08d5"]
|
||||||
|
|
||||||
|
[node name="MasterOptionsMenu" type="Control"]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
script = ExtResource("1_u08d5")
|
||||||
|
|
||||||
|
[node name="TabContainer" type="TabContainer" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
tab_alignment = 1
|
@ -0,0 +1,25 @@
|
|||||||
|
[gd_scene load_steps=5 format=3 uid="uid://hmx6o472ropw"]
|
||||||
|
|
||||||
|
[ext_resource type="PackedScene" uid="uid://bvwl11s2p0hd" path="res://addons/maaacks_game_template/base/scenes/menus/options_menu/master_options_menu.tscn" id="1_uaidt"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://dp3rgqaehb3xu" path="res://addons/maaacks_game_template/base/scenes/menus/options_menu/input/input_options_menu.tscn" id="2_15wl6"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://c8vnncjwqcpab" path="res://addons/maaacks_game_template/base/scenes/menus/options_menu/audio/audio_options_menu.tscn" id="3_qg4me"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://b2numvphf2kau" path="res://addons/maaacks_game_template/base/scenes/menus/options_menu/video/video_options_menu.tscn" id="4_1t848"]
|
||||||
|
|
||||||
|
[node name="MasterOptionsMenu" instance=ExtResource("1_uaidt")]
|
||||||
|
|
||||||
|
[node name="TabContainer" parent="." index="0"]
|
||||||
|
current_tab = 0
|
||||||
|
|
||||||
|
[node name="Controls" parent="TabContainer" index="1" instance=ExtResource("2_15wl6")]
|
||||||
|
layout_mode = 2
|
||||||
|
metadata/_tab_index = 0
|
||||||
|
|
||||||
|
[node name="Audio" parent="TabContainer" index="2" instance=ExtResource("3_qg4me")]
|
||||||
|
visible = false
|
||||||
|
layout_mode = 2
|
||||||
|
metadata/_tab_index = 1
|
||||||
|
|
||||||
|
[node name="Video" parent="TabContainer" index="3" instance=ExtResource("4_1t848")]
|
||||||
|
visible = false
|
||||||
|
layout_mode = 2
|
||||||
|
metadata/_tab_index = 2
|
@ -0,0 +1,45 @@
|
|||||||
|
class_name MiniOptionsMenu
|
||||||
|
extends Control
|
||||||
|
|
||||||
|
@onready var mute_control = %MuteControl
|
||||||
|
@onready var fullscreen_control = %FullscreenControl
|
||||||
|
|
||||||
|
@export var audio_control_scene : PackedScene
|
||||||
|
@export var hide_busses : Array[String]
|
||||||
|
|
||||||
|
func _on_bus_changed(bus_value : float, bus_iter : int) -> void:
|
||||||
|
AppSettings.set_bus_volume(bus_iter, bus_value)
|
||||||
|
|
||||||
|
func _add_audio_control(bus_name : String, bus_value : float, bus_iter : int) -> void:
|
||||||
|
if audio_control_scene == null or bus_name in hide_busses or bus_name.begins_with(AppSettings.SYSTEM_BUS_NAME_PREFIX):
|
||||||
|
return
|
||||||
|
var audio_control = audio_control_scene.instantiate()
|
||||||
|
%AudioControlContainer.call_deferred("add_child", audio_control)
|
||||||
|
if audio_control is OptionControl:
|
||||||
|
audio_control.option_section = OptionControl.OptionSections.AUDIO
|
||||||
|
audio_control.option_name = bus_name
|
||||||
|
audio_control.value = bus_value
|
||||||
|
audio_control.connect("setting_changed", _on_bus_changed.bind(bus_iter))
|
||||||
|
|
||||||
|
func _add_audio_bus_controls() -> void:
|
||||||
|
for bus_iter in AudioServer.bus_count:
|
||||||
|
var bus_name : String = AppSettings.get_audio_bus_name(bus_iter)
|
||||||
|
var linear : float = AppSettings.get_bus_volume(bus_iter)
|
||||||
|
_add_audio_control(bus_name, linear, bus_iter)
|
||||||
|
|
||||||
|
func _update_ui() -> void:
|
||||||
|
_add_audio_bus_controls()
|
||||||
|
mute_control.value = AppSettings.is_muted()
|
||||||
|
fullscreen_control.value = AppSettings.is_fullscreen(get_window())
|
||||||
|
|
||||||
|
func _sync_with_config() -> void:
|
||||||
|
_update_ui()
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
_sync_with_config()
|
||||||
|
|
||||||
|
func _on_mute_control_setting_changed(value : bool) -> void:
|
||||||
|
AppSettings.set_mute(value)
|
||||||
|
|
||||||
|
func _on_fullscreen_control_setting_changed(value : bool) -> void:
|
||||||
|
AppSettings.set_fullscreen_enabled(value, get_window())
|
@ -0,0 +1 @@
|
|||||||
|
uid://1c0iyo5djoxj
|
@ -0,0 +1,51 @@
|
|||||||
|
[gd_scene load_steps=5 format=3 uid="uid://vh1ucj2rfbby"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://1c0iyo5djoxj" path="res://addons/maaacks_game_template/base/scenes/menus/options_menu/mini_options_menu.gd" id="1_32vm2"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://cl416gdb1fgwr" path="res://addons/maaacks_game_template/base/scenes/menus/options_menu/option_control/slider_option_control.tscn" id="2_kpc65"]
|
||||||
|
[ext_resource type="Script" uid="uid://1nf36h0gms3q" path="res://addons/maaacks_game_template/base/scripts/capture_focus.gd" id="3_7qt1o"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://bsxh6v7j0257h" path="res://addons/maaacks_game_template/base/scenes/menus/options_menu/option_control/toggle_option_control.tscn" id="4_b20fb"]
|
||||||
|
|
||||||
|
[node name="MiniOptionsMenu" type="VBoxContainer"]
|
||||||
|
custom_minimum_size = Vector2(400, 260)
|
||||||
|
anchors_preset = 8
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = -200.0
|
||||||
|
offset_top = -130.0
|
||||||
|
offset_right = 200.0
|
||||||
|
offset_bottom = 130.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
size_flags_horizontal = 4
|
||||||
|
theme_override_constants/separation = 8
|
||||||
|
alignment = 1
|
||||||
|
script = ExtResource("1_32vm2")
|
||||||
|
audio_control_scene = ExtResource("2_kpc65")
|
||||||
|
|
||||||
|
[node name="AudioControlContainer" type="VBoxContainer" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/separation = 8
|
||||||
|
script = ExtResource("3_7qt1o")
|
||||||
|
search_depth = 2
|
||||||
|
|
||||||
|
[node name="MuteControl" parent="." instance=ExtResource("4_b20fb")]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
option_name = "Mute"
|
||||||
|
option_section = 2
|
||||||
|
key = "Mute"
|
||||||
|
section = "AudioSettings"
|
||||||
|
|
||||||
|
[node name="FullscreenControl" parent="." instance=ExtResource("4_b20fb")]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
option_name = "Fullscreen"
|
||||||
|
option_section = 3
|
||||||
|
key = "FullscreenEnabled"
|
||||||
|
section = "VideoSettings"
|
||||||
|
|
||||||
|
[connection signal="setting_changed" from="MuteControl" to="." method="_on_mute_control_setting_changed"]
|
||||||
|
[connection signal="setting_changed" from="FullscreenControl" to="." method="_on_fullscreen_control_setting_changed"]
|
@ -0,0 +1,81 @@
|
|||||||
|
@tool
|
||||||
|
class_name ListOptionControl
|
||||||
|
extends OptionControl
|
||||||
|
|
||||||
|
## Locks Option Titles from auto-updating when editing Option Values.
|
||||||
|
## Intentionally put first for initialization.
|
||||||
|
@export var lock_titles : bool = false
|
||||||
|
## Defines the list of possible values for the variable
|
||||||
|
## this option stores in the config file.
|
||||||
|
@export var option_values : Array :
|
||||||
|
set(value) :
|
||||||
|
option_values = value
|
||||||
|
_on_option_values_changed()
|
||||||
|
|
||||||
|
## Defines the list of options displayed to the user.
|
||||||
|
## Length should match with Option Values.
|
||||||
|
@export var option_titles : Array[String] :
|
||||||
|
set(value):
|
||||||
|
option_titles = value
|
||||||
|
if is_inside_tree():
|
||||||
|
_set_option_list(option_titles)
|
||||||
|
|
||||||
|
var custom_option_values : Array
|
||||||
|
|
||||||
|
func _on_option_values_changed() -> void:
|
||||||
|
if option_values.is_empty(): return
|
||||||
|
custom_option_values = option_values.duplicate()
|
||||||
|
var first_value = custom_option_values.front()
|
||||||
|
property_type = typeof(first_value)
|
||||||
|
_set_titles_from_values()
|
||||||
|
|
||||||
|
func _on_setting_changed(value : Variant) -> void:
|
||||||
|
if value < custom_option_values.size() and value >= 0:
|
||||||
|
super._on_setting_changed(custom_option_values[value])
|
||||||
|
|
||||||
|
func _set_titles_from_values() -> void:
|
||||||
|
if lock_titles: return
|
||||||
|
var mapped_titles : Array[String] = []
|
||||||
|
for option_value in custom_option_values:
|
||||||
|
mapped_titles.append(_value_title_map(option_value))
|
||||||
|
option_titles = mapped_titles
|
||||||
|
|
||||||
|
func _value_title_map(value : Variant) -> String:
|
||||||
|
return "%s" % value
|
||||||
|
|
||||||
|
func _match_value_to_other(value : Variant, other : Variant) -> Variant:
|
||||||
|
# Primarily for when the editor saves floats as ints instead
|
||||||
|
if value is int and other is float:
|
||||||
|
return float(value)
|
||||||
|
if value is float and other is int:
|
||||||
|
return int(round(value))
|
||||||
|
return value
|
||||||
|
|
||||||
|
func _set_value(value : Variant) -> Variant:
|
||||||
|
if option_values.is_empty(): return
|
||||||
|
if value == null:
|
||||||
|
return super._set_value(-1)
|
||||||
|
custom_option_values = option_values.duplicate()
|
||||||
|
value = _match_value_to_other(value, custom_option_values.front())
|
||||||
|
if value not in custom_option_values and typeof(value) == property_type:
|
||||||
|
custom_option_values.append(value)
|
||||||
|
custom_option_values.sort()
|
||||||
|
_set_titles_from_values()
|
||||||
|
if value not in option_values:
|
||||||
|
disable_option(custom_option_values.find(value))
|
||||||
|
value = custom_option_values.find(value)
|
||||||
|
return super._set_value(value)
|
||||||
|
|
||||||
|
func _set_option_list(option_titles_list : Array) -> void:
|
||||||
|
%OptionButton.clear()
|
||||||
|
for option_title in option_titles_list:
|
||||||
|
%OptionButton.add_item(option_title)
|
||||||
|
|
||||||
|
func disable_option(option_index : int, disabled : bool = true) -> void:
|
||||||
|
%OptionButton.set_item_disabled(option_index, disabled)
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
lock_titles = lock_titles
|
||||||
|
option_titles = option_titles
|
||||||
|
option_values = option_values
|
||||||
|
super._ready()
|
@ -0,0 +1 @@
|
|||||||
|
uid://b8xqufg4re3c2
|
@ -0,0 +1,14 @@
|
|||||||
|
[gd_scene load_steps=3 format=3 uid="uid://b6bl3n5mp3m1e"]
|
||||||
|
|
||||||
|
[ext_resource type="PackedScene" uid="uid://d7te75il06t7" path="res://addons/maaacks_game_template/base/scenes/menus/options_menu/option_control/option_control.tscn" id="1_blo3b"]
|
||||||
|
[ext_resource type="Script" uid="uid://b8xqufg4re3c2" path="res://addons/maaacks_game_template/base/scenes/menus/options_menu/option_control/list_option_control.gd" id="2_kt4vl"]
|
||||||
|
|
||||||
|
[node name="OptionControl" instance=ExtResource("1_blo3b")]
|
||||||
|
script = ExtResource("2_kt4vl")
|
||||||
|
lock_titles = false
|
||||||
|
option_values = []
|
||||||
|
option_titles = []
|
||||||
|
|
||||||
|
[node name="OptionButton" type="OptionButton" parent="." index="1"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
@ -0,0 +1,140 @@
|
|||||||
|
@tool
|
||||||
|
class_name OptionControl
|
||||||
|
extends Control
|
||||||
|
|
||||||
|
signal setting_changed(value)
|
||||||
|
|
||||||
|
enum OptionSections{
|
||||||
|
NONE,
|
||||||
|
INPUT,
|
||||||
|
AUDIO,
|
||||||
|
VIDEO,
|
||||||
|
GAME,
|
||||||
|
APPLICATION,
|
||||||
|
CUSTOM,
|
||||||
|
}
|
||||||
|
|
||||||
|
const OptionSectionNames : Dictionary = {
|
||||||
|
OptionSections.NONE : "",
|
||||||
|
OptionSections.INPUT : AppSettings.INPUT_SECTION,
|
||||||
|
OptionSections.AUDIO : AppSettings.AUDIO_SECTION,
|
||||||
|
OptionSections.VIDEO : AppSettings.VIDEO_SECTION,
|
||||||
|
OptionSections.GAME : AppSettings.GAME_SECTION,
|
||||||
|
OptionSections.APPLICATION : AppSettings.APPLICATION_SECTION,
|
||||||
|
OptionSections.CUSTOM : AppSettings.CUSTOM_SECTION,
|
||||||
|
}
|
||||||
|
|
||||||
|
## Locks config names in case of issues with inherited scenes.
|
||||||
|
## Intentionally put first for initialization.
|
||||||
|
@export var lock_config_names : bool = false
|
||||||
|
## Defines text displayed to the user.
|
||||||
|
@export var option_name : String :
|
||||||
|
set(value):
|
||||||
|
var _update_config : bool = option_name.to_pascal_case() == key and not lock_config_names
|
||||||
|
option_name = value
|
||||||
|
if is_inside_tree():
|
||||||
|
%OptionLabel.text = "%s%s" % [option_name, label_suffix]
|
||||||
|
if _update_config:
|
||||||
|
key = option_name.to_pascal_case()
|
||||||
|
## Defines what section in the config file this option belongs under.
|
||||||
|
@export var option_section : OptionSections :
|
||||||
|
set(value):
|
||||||
|
var _update_config : bool = OptionSectionNames[option_section] == section and not lock_config_names
|
||||||
|
option_section = value
|
||||||
|
if _update_config:
|
||||||
|
section = OptionSectionNames[option_section]
|
||||||
|
|
||||||
|
@export_group("Config Names")
|
||||||
|
## Defines the key for this option variable in the config file.
|
||||||
|
@export var key : String
|
||||||
|
## Defines the section for this option variable in the config file.
|
||||||
|
@export var section : String
|
||||||
|
@export_group("Format")
|
||||||
|
@export var label_suffix : String = " :"
|
||||||
|
@export_group("Properties")
|
||||||
|
## Defines whether the option is editable, or only visible by the user.
|
||||||
|
@export var editable : bool = true : set = set_editable
|
||||||
|
## Defines what kind of variable this option stores in the config file.
|
||||||
|
@export var property_type : Variant.Type = TYPE_BOOL
|
||||||
|
|
||||||
|
## It is advised to use an external editor to set the default value in the scene file.
|
||||||
|
## Godot can experience a bug (caching issue?) that may undo changes.
|
||||||
|
var default_value
|
||||||
|
var _connected_nodes : Array
|
||||||
|
|
||||||
|
func _on_setting_changed(value) -> void:
|
||||||
|
if Engine.is_editor_hint(): return
|
||||||
|
Config.set_config(section, key, value)
|
||||||
|
setting_changed.emit(value)
|
||||||
|
|
||||||
|
func _get_setting(default : Variant = null) -> Variant:
|
||||||
|
return Config.get_config(section, key, default)
|
||||||
|
|
||||||
|
func _connect_option_inputs(node) -> void:
|
||||||
|
if node in _connected_nodes: return
|
||||||
|
if node is Button:
|
||||||
|
if node is OptionButton:
|
||||||
|
node.item_selected.connect(_on_setting_changed)
|
||||||
|
elif node is ColorPickerButton:
|
||||||
|
node.color_changed.connect(_on_setting_changed)
|
||||||
|
else:
|
||||||
|
node.toggled.connect(_on_setting_changed)
|
||||||
|
_connected_nodes.append(node)
|
||||||
|
if node is Range:
|
||||||
|
node.value_changed.connect(_on_setting_changed)
|
||||||
|
_connected_nodes.append(node)
|
||||||
|
if node is LineEdit or node is TextEdit:
|
||||||
|
node.text_changed.connect(_on_setting_changed)
|
||||||
|
_connected_nodes.append(node)
|
||||||
|
|
||||||
|
func _set_value(value : Variant) -> Variant:
|
||||||
|
if value == null:
|
||||||
|
return
|
||||||
|
for node in get_children():
|
||||||
|
if node is Button:
|
||||||
|
if node is OptionButton:
|
||||||
|
node.select(value as int)
|
||||||
|
elif node is ColorPickerButton:
|
||||||
|
node.color = value as Color
|
||||||
|
else:
|
||||||
|
node.button_pressed = value as bool
|
||||||
|
if node is Range:
|
||||||
|
node.value = value as float
|
||||||
|
if node is LineEdit or node is TextEdit:
|
||||||
|
node.text = "%s" % value
|
||||||
|
return value
|
||||||
|
|
||||||
|
func set_value(value : Variant) -> void:
|
||||||
|
value = _set_value(value)
|
||||||
|
_on_setting_changed(value)
|
||||||
|
|
||||||
|
func set_editable(value : bool = true) -> void:
|
||||||
|
editable = value
|
||||||
|
for node in get_children():
|
||||||
|
if node is Button:
|
||||||
|
node.disabled = !editable
|
||||||
|
if node is Slider or node is SpinBox or node is LineEdit or node is TextEdit:
|
||||||
|
node.editable = editable
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
lock_config_names = lock_config_names
|
||||||
|
option_section = option_section
|
||||||
|
option_name = option_name
|
||||||
|
property_type = property_type
|
||||||
|
default_value = default_value
|
||||||
|
_set_value(_get_setting(default_value))
|
||||||
|
for child in get_children():
|
||||||
|
_connect_option_inputs(child)
|
||||||
|
child_entered_tree.connect(_connect_option_inputs)
|
||||||
|
|
||||||
|
func _set(property : StringName, value : Variant) -> bool:
|
||||||
|
if property == "value":
|
||||||
|
set_value(value)
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
|
||||||
|
func _get_property_list() -> Array[Dictionary]:
|
||||||
|
return [
|
||||||
|
{ "name": "value", "type": property_type, "usage": PROPERTY_USAGE_NONE},
|
||||||
|
{ "name": "default_value", "type": property_type}
|
||||||
|
]
|