diff --git a/addons/maaacks_game_template/ATTRIBUTION.md b/addons/maaacks_game_template/ATTRIBUTION.md new file mode 100644 index 0000000..02d28a5 --- /dev/null +++ b/addons/maaacks_game_template/ATTRIBUTION.md @@ -0,0 +1,37 @@ +# Attribution +## Collaborators + +### Godot Minimal Game Template +![Maaack Plugin Icon](/addons/maaacks_game_template/assets/plugin_logo/logo.png) +Author: [Marek Belski and contributors](https://github.com/Maaack/Godot-Minimal-Game-Template/graphs/contributors) +Source: [github: Godot-Minimal-Game-Template](https://github.com/Maaack/Godot-Minimal-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 +![Godot Engine Logo](/addons/maaacks_game_template/assets/godot_engine_logo/logo_vertical_color_dark.png) +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 +![Git Logo](/addons/maaacks_game_template/assets/git_logo/Git-Logo-2Color.png) +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) diff --git a/addons/maaacks_game_template/LICENSE.txt b/addons/maaacks_game_template/LICENSE.txt new file mode 100644 index 0000000..935618d --- /dev/null +++ b/addons/maaacks_game_template/LICENSE.txt @@ -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. diff --git a/addons/maaacks_game_template/README.md b/addons/maaacks_game_template/README.md new file mode 100644 index 0000000..0ecccca --- /dev/null +++ b/addons/maaacks_game_template/README.md @@ -0,0 +1,176 @@ +# Godot Minimal Game Template +For Godot 4.5 (4.3+ 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-minimal-game-template) + +[Featured Games](#featured-games) + +### Videos + +[![Quick Intro Video](https://img.youtube.com/vi/U9CB3vKINVw/hqdefault.jpg)](https://youtu.be/U9CB3vKINVw) +[More Videos](/addons/maaacks_game_template/docs/Videos.md) + +### Screenshots +![Main Menu](/addons/maaacks_game_template/media/screenshot-6-main-menu-5.png) +![Key Rebinding](/addons/maaacks_game_template/media/screenshot-6-input-list-8.png) +![Audio Controls](/addons/maaacks_game_template/media/screenshot-6-audio-options-2.png) +![Video Controls](/addons/maaacks_game_template/media/screenshot-6-video-options-5.png) +![Pause Menu](/addons/maaacks_game_template/media/screenshot-6-pause-menu-3.png) +[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 +- Opening Scene +- Persistent Settings +- Simple Config Interface +- Extensible Overlay Menus +- Keyboard/Mouse Support +- Gamepad Support + +### Extras + +The `extras/` folder holds components that extend the core application. + +- Level Loaders +- Level Progress Manager +- Win / Lose Manager +- 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/`. + +- Game Scene +- Level Class & 3 Levels +- Tutorial Windows & 3 Tutorial Messages +- Win & Lose Windows +- Master Options Menu +- End Credits +- Main Menu w/ Animations +- Opening w/ Godot Logo + +### Full + +Users that want a more complete set of features can try [Maaack's Game Template](https://github.com/Maaack/Godot-Game-Template) or other options from the [plugin suite](/addons/maaacks_game_template/docs/PluginSuite.md). + +The full Game Template includes: +- Loading Screen +- Game State Management (Basic Saving/Loading) +- UI Sound Controller (Button SFX) +- Background Music Controller +- Credits Reader (Markdown File Parser) +- Globals Config Autoload + +## 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. + +![Package Icon](/addons/maaacks_game_template/media/mini-game-icon-black-transparent-256x256.png) + +When starting a new project: + +1. Go to the `Asset Library Projects` tab. +2. Search for "Maaack's Minimal 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 Minimal 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-Minimal-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) +[Add Custom Options](/addons/maaacks_game_template/docs/AddingCustomOptions.md) +[How Parts Work](/addons/maaacks_game_template/docs/HowPartsWork.md) +[Moving Files](/addons/maaacks_game_template/docs/MovingFiles.md) +[Uploading to itch.io](/addons/maaacks_game_template/docs/UploadingToItchIo.md) +[Build and Publish Your Game Using CICD](/addons/maaacks_game_template/docs/BuildAndPublish.md) +[Automatic Updating](/addons/maaacks_game_template/docs/AutomaticUpdating.md) + +--- + +## Featured Games + +| Baking Godium | Spud Customs | Rent Seek Kill | +| :-------:| :-------: | :-------: | +| ![Baking Godium](/addons/maaacks_game_template/media/thumbnail-game-baking-godium.png) | ![Spud Customs](/addons/maaacks_game_template/media/thumbnail-game-spud-customs.png) | ![Rent-Seek-Kill](/addons/maaacks_game_template/media/thumbnail-game-rent-seek-kill.png) | +| [Play on itch.io](https://maaack.itch.io/baking-godium) | [Find on Steam](https://store.steampowered.com/app/3291880/Spud_Customs/) | [Play on itch.io](https://xandruher.itch.io/rent-seek-kill) | + + +[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/4657) +[Godot Asset Library - Plugin](https://godotengine.org/asset-library/asset/4658) diff --git a/addons/maaacks_game_template/assets/git_logo/Git-Logo-2Color.png b/addons/maaacks_game_template/assets/git_logo/Git-Logo-2Color.png new file mode 100644 index 0000000..18c5b29 Binary files /dev/null and b/addons/maaacks_game_template/assets/git_logo/Git-Logo-2Color.png differ diff --git a/addons/maaacks_game_template/assets/git_logo/Git-Logo-2Color.png.import b/addons/maaacks_game_template/assets/git_logo/Git-Logo-2Color.png.import new file mode 100644 index 0000000..a052ea9 --- /dev/null +++ b/addons/maaacks_game_template/assets/git_logo/Git-Logo-2Color.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dgrp8dlvkku4j" +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/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/maaacks_game_template/assets/git_logo/LICENSE.txt b/addons/maaacks_game_template/assets/git_logo/LICENSE.txt new file mode 100644 index 0000000..2d17b1d --- /dev/null +++ b/addons/maaacks_game_template/assets/git_logo/LICENSE.txt @@ -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/ + diff --git a/addons/maaacks_game_template/assets/godot_engine_logo/LICENSE.txt b/addons/maaacks_game_template/assets/godot_engine_logo/LICENSE.txt new file mode 100644 index 0000000..a081c9e --- /dev/null +++ b/addons/maaacks_game_template/assets/godot_engine_logo/LICENSE.txt @@ -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/ \ No newline at end of file diff --git a/addons/maaacks_game_template/assets/godot_engine_logo/logo_vertical_color_dark.png b/addons/maaacks_game_template/assets/godot_engine_logo/logo_vertical_color_dark.png new file mode 100644 index 0000000..2c38732 Binary files /dev/null and b/addons/maaacks_game_template/assets/godot_engine_logo/logo_vertical_color_dark.png differ diff --git a/addons/maaacks_game_template/assets/godot_engine_logo/logo_vertical_color_dark.png.import b/addons/maaacks_game_template/assets/godot_engine_logo/logo_vertical_color_dark.png.import new file mode 100644 index 0000000..d1f83d5 --- /dev/null +++ b/addons/maaacks_game_template/assets/godot_engine_logo/logo_vertical_color_dark.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://0l0oarduihwi" +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/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/maaacks_game_template/assets/input-icons/License.txt b/addons/maaacks_game_template/assets/input-icons/License.txt new file mode 100644 index 0000000..bd22217 --- /dev/null +++ b/addons/maaacks_game_template/assets/input-icons/License.txt @@ -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 \ No newline at end of file diff --git a/addons/maaacks_game_template/assets/input-icons/icons-filled-colored-2x.png b/addons/maaacks_game_template/assets/input-icons/icons-filled-colored-2x.png new file mode 100644 index 0000000..d9d8fbd Binary files /dev/null and b/addons/maaacks_game_template/assets/input-icons/icons-filled-colored-2x.png differ diff --git a/addons/maaacks_game_template/assets/input-icons/icons-filled-colored-2x.png.import b/addons/maaacks_game_template/assets/input-icons/icons-filled-colored-2x.png.import new file mode 100644 index 0000000..7b0b8d4 --- /dev/null +++ b/addons/maaacks_game_template/assets/input-icons/icons-filled-colored-2x.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://nqdhl41h0tpv" +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/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/maaacks_game_template/assets/input-icons/icons-filled-colored-vector.svg b/addons/maaacks_game_template/assets/input-icons/icons-filled-colored-vector.svg new file mode 100644 index 0000000..3b660d9 --- /dev/null +++ b/addons/maaacks_game_template/assets/input-icons/icons-filled-colored-vector.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/addons/maaacks_game_template/assets/input-icons/icons-filled-colored-vector.svg.import b/addons/maaacks_game_template/assets/input-icons/icons-filled-colored-vector.svg.import new file mode 100644 index 0000000..2da6eec --- /dev/null +++ b/addons/maaacks_game_template/assets/input-icons/icons-filled-colored-vector.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cp7xvu3oujlnj" +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/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/maaacks_game_template/assets/input-icons/icons-filled-colored.png b/addons/maaacks_game_template/assets/input-icons/icons-filled-colored.png new file mode 100644 index 0000000..e7ed3fe Binary files /dev/null and b/addons/maaacks_game_template/assets/input-icons/icons-filled-colored.png differ diff --git a/addons/maaacks_game_template/assets/input-icons/icons-filled-colored.png.import b/addons/maaacks_game_template/assets/input-icons/icons-filled-colored.png.import new file mode 100644 index 0000000..2b8e624 --- /dev/null +++ b/addons/maaacks_game_template/assets/input-icons/icons-filled-colored.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dxvbsbs428nj7" +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/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/maaacks_game_template/assets/input-icons/icons-filled-white-2x.png b/addons/maaacks_game_template/assets/input-icons/icons-filled-white-2x.png new file mode 100644 index 0000000..2399fc2 Binary files /dev/null and b/addons/maaacks_game_template/assets/input-icons/icons-filled-white-2x.png differ diff --git a/addons/maaacks_game_template/assets/input-icons/icons-filled-white-2x.png.import b/addons/maaacks_game_template/assets/input-icons/icons-filled-white-2x.png.import new file mode 100644 index 0000000..097c9f6 --- /dev/null +++ b/addons/maaacks_game_template/assets/input-icons/icons-filled-white-2x.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://df0qisbxrhsqa" +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/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/maaacks_game_template/assets/input-icons/icons-filled-white-vector.svg b/addons/maaacks_game_template/assets/input-icons/icons-filled-white-vector.svg new file mode 100644 index 0000000..b8ede8d --- /dev/null +++ b/addons/maaacks_game_template/assets/input-icons/icons-filled-white-vector.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/addons/maaacks_game_template/assets/input-icons/icons-filled-white-vector.svg.import b/addons/maaacks_game_template/assets/input-icons/icons-filled-white-vector.svg.import new file mode 100644 index 0000000..e1a9c97 --- /dev/null +++ b/addons/maaacks_game_template/assets/input-icons/icons-filled-white-vector.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dl5p5qw3hp8y" +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/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/maaacks_game_template/assets/input-icons/icons-filled-white.png b/addons/maaacks_game_template/assets/input-icons/icons-filled-white.png new file mode 100644 index 0000000..281f3e6 Binary files /dev/null and b/addons/maaacks_game_template/assets/input-icons/icons-filled-white.png differ diff --git a/addons/maaacks_game_template/assets/input-icons/icons-filled-white.png.import b/addons/maaacks_game_template/assets/input-icons/icons-filled-white.png.import new file mode 100644 index 0000000..9145e84 --- /dev/null +++ b/addons/maaacks_game_template/assets/input-icons/icons-filled-white.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://5gjytq6nlww3" +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/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/maaacks_game_template/assets/input-icons/icons-outlined-colored-2x.png b/addons/maaacks_game_template/assets/input-icons/icons-outlined-colored-2x.png new file mode 100644 index 0000000..9699da0 Binary files /dev/null and b/addons/maaacks_game_template/assets/input-icons/icons-outlined-colored-2x.png differ diff --git a/addons/maaacks_game_template/assets/input-icons/icons-outlined-colored-2x.png.import b/addons/maaacks_game_template/assets/input-icons/icons-outlined-colored-2x.png.import new file mode 100644 index 0000000..f0ec55f --- /dev/null +++ b/addons/maaacks_game_template/assets/input-icons/icons-outlined-colored-2x.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://1gr33hl6p8tu" +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/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/maaacks_game_template/assets/input-icons/icons-outlined-colored-vector.svg b/addons/maaacks_game_template/assets/input-icons/icons-outlined-colored-vector.svg new file mode 100644 index 0000000..10b9658 --- /dev/null +++ b/addons/maaacks_game_template/assets/input-icons/icons-outlined-colored-vector.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/addons/maaacks_game_template/assets/input-icons/icons-outlined-colored-vector.svg.import b/addons/maaacks_game_template/assets/input-icons/icons-outlined-colored-vector.svg.import new file mode 100644 index 0000000..82d0a6a --- /dev/null +++ b/addons/maaacks_game_template/assets/input-icons/icons-outlined-colored-vector.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://2xcu58kisr45" +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/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/maaacks_game_template/assets/input-icons/icons-outlined-colored.png b/addons/maaacks_game_template/assets/input-icons/icons-outlined-colored.png new file mode 100644 index 0000000..6aa572a Binary files /dev/null and b/addons/maaacks_game_template/assets/input-icons/icons-outlined-colored.png differ diff --git a/addons/maaacks_game_template/assets/input-icons/icons-outlined-colored.png.import b/addons/maaacks_game_template/assets/input-icons/icons-outlined-colored.png.import new file mode 100644 index 0000000..d1e5f53 --- /dev/null +++ b/addons/maaacks_game_template/assets/input-icons/icons-outlined-colored.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cj4f8dma2647n" +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/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/maaacks_game_template/assets/input-icons/icons-outlined-white-2x.png b/addons/maaacks_game_template/assets/input-icons/icons-outlined-white-2x.png new file mode 100644 index 0000000..8dd7cb9 Binary files /dev/null and b/addons/maaacks_game_template/assets/input-icons/icons-outlined-white-2x.png differ diff --git a/addons/maaacks_game_template/assets/input-icons/icons-outlined-white-2x.png.import b/addons/maaacks_game_template/assets/input-icons/icons-outlined-white-2x.png.import new file mode 100644 index 0000000..5a0614b --- /dev/null +++ b/addons/maaacks_game_template/assets/input-icons/icons-outlined-white-2x.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://betu06qbgx5gf" +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/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/maaacks_game_template/assets/input-icons/icons-outlined-white-vector.svg b/addons/maaacks_game_template/assets/input-icons/icons-outlined-white-vector.svg new file mode 100644 index 0000000..17121c7 --- /dev/null +++ b/addons/maaacks_game_template/assets/input-icons/icons-outlined-white-vector.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/addons/maaacks_game_template/assets/input-icons/icons-outlined-white-vector.svg.import b/addons/maaacks_game_template/assets/input-icons/icons-outlined-white-vector.svg.import new file mode 100644 index 0000000..79327f5 --- /dev/null +++ b/addons/maaacks_game_template/assets/input-icons/icons-outlined-white-vector.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cf4s4f7tvltpp" +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/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/maaacks_game_template/assets/input-icons/icons-outlined-white.png b/addons/maaacks_game_template/assets/input-icons/icons-outlined-white.png new file mode 100644 index 0000000..3b95729 Binary files /dev/null and b/addons/maaacks_game_template/assets/input-icons/icons-outlined-white.png differ diff --git a/addons/maaacks_game_template/assets/input-icons/icons-outlined-white.png.import b/addons/maaacks_game_template/assets/input-icons/icons-outlined-white.png.import new file mode 100644 index 0000000..94c298d --- /dev/null +++ b/addons/maaacks_game_template/assets/input-icons/icons-outlined-white.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cj524k2rdjvjo" +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/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/maaacks_game_template/assets/plugin_logo/LICENSE.txt b/addons/maaacks_game_template/assets/plugin_logo/LICENSE.txt new file mode 100644 index 0000000..0cad3a2 --- /dev/null +++ b/addons/maaacks_game_template/assets/plugin_logo/LICENSE.txt @@ -0,0 +1,5 @@ +Maaack's Minimal Game Template Logo +Copyright (c) Marek Belski + +This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International +license (CC BY-NC-ND 4.0 International): https://creativecommons.org/licenses/by-nc-nd/4.0 diff --git a/addons/maaacks_game_template/assets/plugin_logo/logo.png b/addons/maaacks_game_template/assets/plugin_logo/logo.png new file mode 100644 index 0000000..f509071 Binary files /dev/null and b/addons/maaacks_game_template/assets/plugin_logo/logo.png differ diff --git a/addons/maaacks_game_template/assets/plugin_logo/logo.png.import b/addons/maaacks_game_template/assets/plugin_logo/logo.png.import new file mode 100644 index 0000000..5d55e76 --- /dev/null +++ b/addons/maaacks_game_template/assets/plugin_logo/logo.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dxl31q1uq5ahg" +path="res://.godot/imported/logo.png-c01870748f534065a9e1f3a8abe16e3c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/maaacks_game_template/assets/plugin_logo/logo.png" +dest_files=["res://.godot/imported/logo.png-c01870748f534065a9e1f3a8abe16e3c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/maaacks_game_template/base/assets/remapping_input_icons/LICENSE.txt b/addons/maaacks_game_template/base/assets/remapping_input_icons/LICENSE.txt new file mode 100644 index 0000000..b69d4ac --- /dev/null +++ b/addons/maaacks_game_template/base/assets/remapping_input_icons/LICENSE.txt @@ -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/ \ No newline at end of file diff --git a/addons/maaacks_game_template/base/assets/remapping_input_icons/addition_symbol.png b/addons/maaacks_game_template/base/assets/remapping_input_icons/addition_symbol.png new file mode 100644 index 0000000..adfeff9 Binary files /dev/null and b/addons/maaacks_game_template/base/assets/remapping_input_icons/addition_symbol.png differ diff --git a/addons/maaacks_game_template/base/assets/remapping_input_icons/addition_symbol.png.import b/addons/maaacks_game_template/base/assets/remapping_input_icons/addition_symbol.png.import new file mode 100644 index 0000000..5fe5d0c --- /dev/null +++ b/addons/maaacks_game_template/base/assets/remapping_input_icons/addition_symbol.png.import @@ -0,0 +1,40 @@ +[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/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/maaacks_game_template/base/assets/remapping_input_icons/subtraction_symbol.png b/addons/maaacks_game_template/base/assets/remapping_input_icons/subtraction_symbol.png new file mode 100644 index 0000000..01df0ee Binary files /dev/null and b/addons/maaacks_game_template/base/assets/remapping_input_icons/subtraction_symbol.png differ diff --git a/addons/maaacks_game_template/base/assets/remapping_input_icons/subtraction_symbol.png.import b/addons/maaacks_game_template/base/assets/remapping_input_icons/subtraction_symbol.png.import new file mode 100644 index 0000000..fec7952 --- /dev/null +++ b/addons/maaacks_game_template/base/assets/remapping_input_icons/subtraction_symbol.png.import @@ -0,0 +1,40 @@ +[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/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/maaacks_game_template/base/nodes/config/app_settings.gd b/addons/maaacks_game_template/base/nodes/config/app_settings.gd new file mode 100644 index 0000000..fb266e4 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/config/app_settings.gd @@ -0,0 +1,188 @@ +class_name AppSettings +extends Node +## Interface to read/write general application settings through [PlayerConfig]. + +const INPUT_SECTION = &'InputSettings' +const AUDIO_SECTION = &'AudioSettings' +const VIDEO_SECTION = &'VideoSettings' +const GAME_SECTION = &'GameSettings' +const APPLICATION_SECTION = &'ApplicationSettings' +const CUSTOM_SECTION = &'CustomSettings' + +const FULLSCREEN = &'Fullscreen' +const SCREEN_RESOLUTION = &'ScreenResolution' +const V_SYNC = &'V-Sync' +const MUTE_SETTING = &'Mute' +const MASTER_BUS_INDEX = 0 +const SYSTEM_BUS_NAME_PREFIX = "_" + +# Input +static var default_action_events : Dictionary +static var initial_bus_volumes : Array + +static func get_config_input_events(action_name : String, default = null) -> Array: + return PlayerConfig.get_config(INPUT_SECTION, action_name, default) + +static func set_config_input_events(action_name : String, inputs : Array) -> void: + PlayerConfig.set_config(INPUT_SECTION, action_name, inputs) + +static func _clear_config_input_events() -> void: + PlayerConfig.erase_section(INPUT_SECTION) + +static func remove_action_input_event(action_name : String, input_event : InputEvent) -> void: + InputMap.action_erase_event(action_name, input_event) + var action_events : Array[InputEvent] = InputMap.action_get_events(action_name) + var config_events : Array = get_config_input_events(action_name, action_events) + config_events.erase(input_event) + set_config_input_events(action_name, config_events) + +static func set_input_from_config(action_name : String) -> void: + var action_events : Array[InputEvent] = InputMap.action_get_events(action_name) + var config_events = get_config_input_events(action_name, action_events) + if config_events == action_events: + return + if config_events.is_empty(): + PlayerConfig.erase_section_key(INPUT_SECTION, action_name) + return + InputMap.action_erase_events(action_name) + for config_event in config_events: + if config_event not in action_events: + InputMap.action_add_event(action_name, config_event) + +static func _get_action_names() -> Array[StringName]: + return InputMap.get_actions() + +static func _get_custom_action_names() -> Array[StringName]: + var callable_filter := func(action_name): return not (action_name.begins_with("ui_") or action_name.begins_with("spatial_editor")) + var action_list := _get_action_names() + return action_list.filter(callable_filter) + +static func get_action_names(built_in_actions : bool = false) -> Array[StringName]: + if built_in_actions: + return _get_action_names() + else: + return _get_custom_action_names() + +static func reset_to_default_inputs() -> void: + _clear_config_input_events() + for action_name in default_action_events: + InputMap.action_erase_events(action_name) + var input_events = default_action_events[action_name] + for input_event in input_events: + InputMap.action_add_event(action_name, input_event) + +static func set_default_inputs() -> void: + var action_list : Array[StringName] = _get_action_names() + for action_name in action_list: + default_action_events[action_name] = InputMap.action_get_events(action_name) + +static func set_inputs_from_config() -> void: + var action_list : Array[StringName] = _get_action_names() + for action_name in action_list: + set_input_from_config(action_name) + +# Audio + +static func get_bus_volume(bus_index : int) -> float: + var initial_linear = 1.0 + if initial_bus_volumes.size() > bus_index: + initial_linear = initial_bus_volumes[bus_index] + var linear = db_to_linear(AudioServer.get_bus_volume_db(bus_index)) + linear /= initial_linear + return linear + +static func set_bus_volume(bus_index : int, linear : float) -> void: + var initial_linear = 1.0 + if initial_bus_volumes.size() > bus_index: + initial_linear = initial_bus_volumes[bus_index] + linear *= initial_linear + AudioServer.set_bus_volume_db(bus_index, linear_to_db(linear)) + +static func is_muted() -> bool: + return AudioServer.is_bus_mute(MASTER_BUS_INDEX) + +static func set_mute(mute_flag : bool) -> void: + AudioServer.set_bus_mute(MASTER_BUS_INDEX, mute_flag) + +static func get_audio_bus_name(bus_iter : int) -> String: + return AudioServer.get_bus_name(bus_iter) + +static func set_audio_from_config() -> void: + for bus_iter in AudioServer.bus_count: + var bus_key : String = get_audio_bus_name(bus_iter).to_pascal_case() + var bus_volume : float = get_bus_volume(bus_iter) + initial_bus_volumes.append(bus_volume) + bus_volume = PlayerConfig.get_config(AUDIO_SECTION, bus_key, bus_volume) + if is_nan(bus_volume): + bus_volume = 1.0 + PlayerConfig.set_config(AUDIO_SECTION, bus_key, bus_volume) + set_bus_volume(bus_iter, bus_volume) + var mute_audio_flag : bool = is_muted() + mute_audio_flag = PlayerConfig.get_config(AUDIO_SECTION, MUTE_SETTING, mute_audio_flag) + set_mute(mute_audio_flag) + +# Video + +static func set_fullscreen_enabled(value : bool, window : Window) -> void: + window.mode = Window.MODE_EXCLUSIVE_FULLSCREEN if (value) else Window.MODE_WINDOWED + +static func set_resolution(value : Vector2i, window : Window, update_config : bool = true) -> void: + if value.x == 0 or value.y == 0: + return + window.size = value + if update_config: + PlayerConfig.set_config(VIDEO_SECTION, SCREEN_RESOLUTION, value) + +static func is_fullscreen(window : Window) -> bool: + return (window.mode == Window.MODE_EXCLUSIVE_FULLSCREEN) or (window.mode == Window.MODE_FULLSCREEN) + +static func get_resolution(window : Window) -> Vector2i: + var current_resolution : Vector2i = window.size + return PlayerConfig.get_config(VIDEO_SECTION, SCREEN_RESOLUTION, current_resolution) + +static func _on_window_size_changed(window: Window) -> void: + PlayerConfig.set_config(VIDEO_SECTION, SCREEN_RESOLUTION, window.size) + +static func _set_fullscreen_from_config(window: Window) -> bool: + var fullscreen_enabled : bool = is_fullscreen(window) + fullscreen_enabled = PlayerConfig.get_config(VIDEO_SECTION, FULLSCREEN, fullscreen_enabled) + set_fullscreen_enabled(fullscreen_enabled, window) + return fullscreen_enabled + +static func set_vsync(vsync_mode : DisplayServer.VSyncMode, window : Window = null) -> void: + var window_id : int = 0 + if window: + window_id = window.get_window_id() + DisplayServer.window_set_vsync_mode(vsync_mode, window_id) + +static func get_vsync(window : Window = null) -> DisplayServer.VSyncMode: + var window_id : int = 0 + if window: + window_id = window.get_window_id() + var vsync_mode = DisplayServer.window_get_vsync_mode(window_id) + return vsync_mode + +static func _set_v_sync_from_config(window: Window) -> DisplayServer.VSyncMode: + var vsync := get_vsync(window) + vsync = PlayerConfig.get_config(VIDEO_SECTION, V_SYNC, vsync) + set_vsync(vsync) + return vsync + +static func set_video_from_config(window : Window) -> void: + window.size_changed.connect(_on_window_size_changed.bind(window)) + var fullscreen_enabled := _set_fullscreen_from_config(window) + if not (fullscreen_enabled or OS.has_feature("web")): + var current_resolution : Vector2i = get_resolution(window) + set_resolution(current_resolution, window) + _set_v_sync_from_config(window) + +# All + +static func set_from_config() -> void: + set_default_inputs() + set_inputs_from_config() + set_audio_from_config() + +static func set_from_config_and_window(window : Window) -> void: + set_from_config() + set_video_from_config(window) diff --git a/addons/maaacks_game_template/base/nodes/config/app_settings.gd.uid b/addons/maaacks_game_template/base/nodes/config/app_settings.gd.uid new file mode 100644 index 0000000..ed60455 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/config/app_settings.gd.uid @@ -0,0 +1 @@ +uid://dwflyh7g2rjxt diff --git a/addons/maaacks_game_template/base/nodes/config/player_config.gd b/addons/maaacks_game_template/base/nodes/config/player_config.gd new file mode 100644 index 0000000..3501a7d --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/config/player_config.gd @@ -0,0 +1,56 @@ +class_name PlayerConfig +extends Object + +## Interface for a single configuration file through [ConfigFile]. + +const CONFIG_FILE_LOCATION := "user://player_config.cfg" + +static var config_file : ConfigFile + +static func _save_config_file() -> void: + var save_error : int = config_file.save(CONFIG_FILE_LOCATION) + if save_error: + push_error("save config file failed with error %d" % save_error) + +static func load_config_file() -> void: + if config_file != null: + return + config_file = ConfigFile.new() + var load_error : int = config_file.load(CONFIG_FILE_LOCATION) + if load_error: + var save_error : int = config_file.save(CONFIG_FILE_LOCATION) + if save_error: + push_error("save config file failed with error %d" % save_error) + +static func set_config(section: String, key: String, value) -> void: + load_config_file() + config_file.set_value(section, key, value) + _save_config_file() + +static func get_config(section: String, key: String, default = null) -> Variant: + load_config_file() + return config_file.get_value(section, key, default) + +static func has_section(section: String) -> bool: + load_config_file() + return config_file.has_section(section) + +static func has_section_key(section: String, key: String) -> bool: + load_config_file() + return config_file.has_section_key(section, key) + +static func erase_section(section: String) -> void: + if has_section(section): + config_file.erase_section(section) + _save_config_file() + +static func erase_section_key(section: String, key: String) -> void: + if has_section_key(section, key): + config_file.erase_section_key(section, key) + _save_config_file() + +static func get_section_keys(section: String) -> PackedStringArray: + load_config_file() + if config_file.has_section(section): + return config_file.get_section_keys(section) + return PackedStringArray() diff --git a/addons/maaacks_game_template/base/nodes/config/player_config.gd.uid b/addons/maaacks_game_template/base/nodes/config/player_config.gd.uid new file mode 100644 index 0000000..37212e7 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/config/player_config.gd.uid @@ -0,0 +1 @@ +uid://dxjk8pgi7yhtq diff --git a/addons/maaacks_game_template/base/nodes/labels/config_name_label.gd b/addons/maaacks_game_template/base/nodes/labels/config_name_label.gd new file mode 100644 index 0000000..085f32a --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/labels/config_name_label.gd @@ -0,0 +1,18 @@ +@tool +extends Label +## Displays the value of `application/config/name`, set in project settings. + +const NO_NAME_STRING : String = "Title" + +## If true, update the title when ready. +@export var auto_update : bool = true + +func update_name_label(): + 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(): + if auto_update: + update_name_label() diff --git a/addons/maaacks_game_template/base/nodes/labels/config_name_label.gd.uid b/addons/maaacks_game_template/base/nodes/labels/config_name_label.gd.uid new file mode 100644 index 0000000..27cd139 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/labels/config_name_label.gd.uid @@ -0,0 +1 @@ +uid://bkwlopi4qn32o diff --git a/addons/maaacks_game_template/base/nodes/labels/config_version_label.gd b/addons/maaacks_game_template/base/nodes/labels/config_version_label.gd new file mode 100644 index 0000000..a7eee38 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/labels/config_version_label.gd @@ -0,0 +1,17 @@ +@tool +extends Label +## 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() diff --git a/addons/maaacks_game_template/base/nodes/labels/config_version_label.gd.uid b/addons/maaacks_game_template/base/nodes/labels/config_version_label.gd.uid new file mode 100644 index 0000000..ef1cc35 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/labels/config_version_label.gd.uid @@ -0,0 +1 @@ +uid://dmkubt2nsnsbn diff --git a/addons/maaacks_game_template/base/nodes/labels/credits_label.gd b/addons/maaacks_game_template/base/nodes/labels/credits_label.gd new file mode 100644 index 0000000..2063b93 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/labels/credits_label.gd @@ -0,0 +1,10 @@ +extends RichTextLabel +## If true, disable opening links. For platforms that don't permit linking to other domains. +@export var disable_opening_links: bool = false + +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: + meta_clicked.connect(_on_meta_clicked) diff --git a/addons/maaacks_game_template/base/nodes/labels/credits_label.gd.uid b/addons/maaacks_game_template/base/nodes/labels/credits_label.gd.uid new file mode 100644 index 0000000..83d735f --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/labels/credits_label.gd.uid @@ -0,0 +1 @@ +uid://cc2wtqasev7le diff --git a/addons/maaacks_game_template/base/nodes/labels/plugin_version_label.gd b/addons/maaacks_game_template/base/nodes/labels/plugin_version_label.gd new file mode 100644 index 0000000..2827c76 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/labels/plugin_version_label.gd @@ -0,0 +1,28 @@ +@tool +extends Label +## Displays the value of `version` from the config file of the specified plugin. + +const NO_VERSION_STRING : String = "0.0.0" + +@export var plugin_directory : String +@export var version_prefix : String = "v" + +func _get_plugin_version() -> String: + if not plugin_directory.is_empty(): + for enabled_plugin in ProjectSettings.get_setting("editor_plugins/enabled"): + if enabled_plugin.contains(plugin_directory): + var config := ConfigFile.new() + var error = config.load(enabled_plugin) + if error != OK: + break + return config.get_value("plugin", "version", NO_VERSION_STRING) + return "" + +func update_version_label() -> void: + var plugin_version = _get_plugin_version() + if plugin_version.is_empty(): + plugin_version = NO_VERSION_STRING + text = version_prefix + plugin_version + +func _ready() -> void: + update_version_label() diff --git a/addons/maaacks_game_template/base/nodes/labels/plugin_version_label.gd.uid b/addons/maaacks_game_template/base/nodes/labels/plugin_version_label.gd.uid new file mode 100644 index 0000000..b9b91f0 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/labels/plugin_version_label.gd.uid @@ -0,0 +1 @@ +uid://kgp5jnrxxhdy diff --git a/addons/maaacks_game_template/base/nodes/menus/main_menu/main_menu.gd b/addons/maaacks_game_template/base/nodes/menus/main_menu/main_menu.gd new file mode 100644 index 0000000..8006e46 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/main_menu/main_menu.gd @@ -0,0 +1,130 @@ +class_name MainMenu +extends Control +## Base menu scene that links to a game scene, an options menu, and credits. + +signal sub_menu_opened +signal sub_menu_closed +signal game_started +signal game_exited + +## Defines the path to the game scene. Hides the play button if empty. +@export_file("*.tscn") var game_scene_path : String +## The scene to open when a player clicks the 'Options' button. +@export var options_packed_scene : PackedScene +## The scene to open when a player clicks the 'Credits' button. +@export var credits_packed_scene : PackedScene +@export var confirm_exit : bool = true +@export_group("Extra Settings") +## If true, signals that the game has started loading in the background, instead of directly loading it. +## Requires Maaack's Scene Loader. +@export var signal_game_start : bool = false +## If true, signals that the player clicked the 'Exit' button, instead of immediately exiting. +@export var signal_game_exit : bool = false + +var sub_menu : Control + +@onready var menu_container = %MenuContainer +@onready var menu_buttons_box_container = %MenuButtonsBoxContainer +@onready var new_game_button = %NewGameButton +@onready var options_button = %OptionsButton +@onready var credits_button = %CreditsButton +@onready var exit_button = %ExitButton +@onready var exit_confirmation = %ExitConfirmation +## If Maaack's Scene Loader is installed, then it will be used to change scenes. +@onready var scene_loader_node = get_tree().root.get_node_or_null(^"SceneLoader") + +func get_game_scene_path() -> String: + return game_scene_path + +func load_game_scene() -> void: + if scene_loader_node: + if signal_game_start: + scene_loader_node.load_scene(get_game_scene_path(), true) + game_started.emit() + else: + scene_loader_node.load_scene(get_game_scene_path()) + else: + get_tree().change_scene_to_file(get_game_scene_path()) + +func new_game() -> void: + load_game_scene() + +func try_exit_game() -> void: + if confirm_exit and (not exit_confirmation.visible): + exit_confirmation.show() + else: + exit_game() + +func exit_game() -> void: + if OS.has_feature("web"): + return + if signal_game_exit: + game_exited.emit() + else: + get_tree().quit() + +func _open_sub_menu(menu : PackedScene) -> Node: + sub_menu = menu.instantiate() + add_child(sub_menu) + menu_container.hide() + sub_menu.hidden.connect(_close_sub_menu, CONNECT_ONE_SHOT) + sub_menu.tree_exiting.connect(_close_sub_menu, CONNECT_ONE_SHOT) + sub_menu_opened.emit() + return sub_menu + +func _close_sub_menu() -> void: + if sub_menu == null: + return + sub_menu.queue_free() + sub_menu = null + menu_container.show() + sub_menu_closed.emit() + +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: + try_exit_game() + if event.is_action_released("ui_accept") and get_viewport().gui_get_focus_owner() == null: + menu_buttons_box_container.focus_first() + +func _hide_exit_for_web() -> void: + if OS.has_feature("web"): + exit_button.hide() + +func _hide_new_game_if_unset() -> void: + if get_game_scene_path().is_empty(): + new_game_button.hide() + +func _hide_options_if_unset() -> void: + if options_packed_scene == null: + options_button.hide() + +func _hide_credits_if_unset() -> void: + if credits_packed_scene == null: + credits_button.hide() + +func _ready() -> void: + _hide_exit_for_web() + _hide_options_if_unset() + _hide_credits_if_unset() + _hide_new_game_if_unset() + +func _on_new_game_button_pressed() -> void: + new_game() + +func _on_options_button_pressed() -> void: + _open_sub_menu(options_packed_scene) + +func _on_credits_button_pressed() -> void: + _open_sub_menu(credits_packed_scene) + +func _on_exit_button_pressed() -> void: + try_exit_game() + +func _on_exit_confirmation_confirmed(): + exit_game() diff --git a/addons/maaacks_game_template/base/nodes/menus/main_menu/main_menu.gd.uid b/addons/maaacks_game_template/base/nodes/menus/main_menu/main_menu.gd.uid new file mode 100644 index 0000000..905b7fd --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/main_menu/main_menu.gd.uid @@ -0,0 +1 @@ +uid://bhgs1upaahk3y diff --git a/addons/maaacks_game_template/base/nodes/menus/main_menu/main_menu.tscn b/addons/maaacks_game_template/base/nodes/menus/main_menu/main_menu.tscn new file mode 100644 index 0000000..2a5b313 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/main_menu/main_menu.tscn @@ -0,0 +1,175 @@ +[gd_scene load_steps=6 format=3 uid="uid://c6k5nnpbypshi"] + +[ext_resource type="Script" uid="uid://bhgs1upaahk3y" path="res://addons/maaacks_game_template/base/nodes/menus/main_menu/main_menu.gd" id="1"] +[ext_resource type="Script" uid="uid://1nf36h0gms3q" path="res://addons/maaacks_game_template/base/nodes/utilities/capture_focus.gd" id="4_l1ebe"] +[ext_resource type="Script" uid="uid://dmkubt2nsnsbn" path="res://addons/maaacks_game_template/base/nodes/labels/config_version_label.gd" id="6_pdiij"] +[ext_resource type="PackedScene" uid="uid://cwt4p3bufkke5" path="res://addons/maaacks_game_template/base/nodes/windows/confirmation_overlaid_window.tscn" id="7_im16j"] +[ext_resource type="Script" uid="uid://bkwlopi4qn32o" path="res://addons/maaacks_game_template/base/nodes/labels/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") + +[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="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 = "Title" +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="VersionMargin" type="MarginContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +theme_override_constants/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="ExitConfirmation" parent="." instance=ExtResource("7_im16j")] +unique_name_in_owner = true +visible = false +custom_minimum_size = Vector2(300, 160) +layout_mode = 1 +offset_left = -150.0 +offset_top = -80.0 +offset_right = 150.0 +offset_bottom = 80.0 +ui_cancel_closes = false +text = "Really exit the game?" +title_visible = false + +[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="confirmed" from="ExitConfirmation" to="." method="_on_exit_confirmation_confirmed"] diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/audio/audio_options_menu.gd b/addons/maaacks_game_template/base/nodes/menus/options_menu/audio/audio_options_menu.gd new file mode 100644 index 0000000..1e0caa5 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/audio/audio_options_menu.gd @@ -0,0 +1,38 @@ +extends Control + +## Scene for adjusting the volume of the audio busses. +@export var audio_control_scene : PackedScene +## Optional names of audio busses that should be ignored. +@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) diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/audio/audio_options_menu.gd.uid b/addons/maaacks_game_template/base/nodes/menus/options_menu/audio/audio_options_menu.gd.uid new file mode 100644 index 0000000..4e3c290 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/audio/audio_options_menu.gd.uid @@ -0,0 +1 @@ +uid://bwugqn2cjr41e diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/audio/audio_options_menu.tscn b/addons/maaacks_game_template/base/nodes/menus/options_menu/audio/audio_options_menu.tscn new file mode 100644 index 0000000..1a9d1f3 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/audio/audio_options_menu.tscn @@ -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/nodes/menus/options_menu/audio/audio_options_menu.gd" id="1"] +[ext_resource type="PackedScene" uid="uid://cl416gdb1fgwr" path="res://addons/maaacks_game_template/base/nodes/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/nodes/utilities/capture_focus.gd" id="3_dtraq"] +[ext_resource type="PackedScene" uid="uid://bsxh6v7j0257h" path="res://addons/maaacks_game_template/base/nodes/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"] diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_actions_list.gd b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_actions_list.gd new file mode 100644 index 0000000..9f77205 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_actions_list.gd @@ -0,0 +1,349 @@ +@tool +class_name InputActionsList +extends Container +## Scene to list the input actions out as buttons in a grid format. + +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" + +## If true, lists action names on the vertical axis. +@export var vertical : bool = true : + set(value): + vertical = value + if is_inside_tree(): + %ParentBoxContainer.vertical = vertical +## The number of inputs to make editable per action name. +@export_range(1, 5) var action_groups : int = 2 +## The header to each input action group. +@export var action_group_names : Array[String] +## The names of the action names that should be listed for editing. +@export var input_action_names : Array[StringName] : + set(value): + var _value_changed = input_action_names != value + input_action_names = value + if _value_changed: + _refresh_readable_action_names() +## The readable names of the action names that should be listed for editing. +@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 +## If true, capitalizes action names in order to make them readable. +@export var capitalize_action_names : bool = true : + set(value): + capitalize_action_names = value + _refresh_readable_action_names() +## If true, show action names that are not explicitely listed in an input action name map. +@export var show_all_actions : bool = true +## Optional minimum size to add to all edit buttons. +@export var button_minimum_size : Vector2 +@export_group("Icons") +## Optional link to an input icon mapper to replace the text with icons. +@export var input_icon_mapper : InputIconMapper +## If true, expand the icons to fill the buttons. +@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 _refresh_readable_action_names(): + var _new_readable_action_names : Array[String] + for action_name in input_action_names: + if capitalize_action_names: + action_name = action_name.capitalize() + _new_readable_action_names.append(action_name) + readable_action_names = _new_readable_action_names + +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 + var button = _get_button_by_action(action_name, action_group) + var readable_input_name : String + if button and button in button_readable_input_map: + readable_input_name = button_readable_input_map[button] + _replace_action(action_name, readable_input_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() + if button_minimum_size.x > 0: + new_label.custom_minimum_size.x = button_minimum_size.x + new_label.size_flags_horizontal = SIZE_SHRINK_CENTER + else: + new_label.size_flags_horizontal = SIZE_EXPAND_FILL + if button_minimum_size.y > 0: + new_label.custom_minimum_size.y = button_minimum_size.y + new_label.size_flags_vertical = SIZE_SHRINK_CENTER + else: + 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, disabled: bool = false) -> void: + var button = _get_button_by_action(action_name, action_group + 1) + if button: + button.disabled = disabled + +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() + if button_minimum_size.x > 0: + new_button.custom_minimum_size.x = button_minimum_size.x + new_button.size_flags_horizontal = SIZE_SHRINK_CENTER + else: + new_button.size_flags_horizontal = SIZE_EXPAND_FILL + if button_minimum_size.y > 0: + new_button.custom_minimum_size.y = button_minimum_size.y + new_button.size_flags_vertical = SIZE_SHRINK_CENTER + else: + new_button.size_flags_vertical = SIZE_EXPAND_FILL + new_button.icon_alignment = HORIZONTAL_ALIGNMENT_CENTER + new_button.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS + 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(action_name : StringName) -> String: + var readable_name : String + if action_name in action_name_map: + readable_name = action_name_map[action_name] + elif action_name in built_in_action_name_map: + readable_name = built_in_action_name_map[action_name] + else: + readable_name = action_name + if capitalize_action_names: + readable_name = readable_name.capitalize() + action_name_map[action_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) + _update_next_button_disabled_state(action_name, group_iter) + group_iter += 1 + while group_iter < action_groups: + _clear_button(action_name, group_iter) + _update_next_button_disabled_state(action_name, group_iter, true) + group_iter += 1 + +func _set_action_box_container_size() -> void: + if button_minimum_size.x > 0: + %ActionBoxContainer.size_flags_horizontal = SIZE_SHRINK_CENTER + else: + %ActionBoxContainer.size_flags_horizontal = SIZE_EXPAND_FILL + if button_minimum_size.y > 0: + %ActionBoxContainer.size_flags_vertical = SIZE_SHRINK_CENTER + else: + %ActionBoxContainer.size_flags_vertical = SIZE_EXPAND_FILL + +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 + _set_action_box_container_size() + _build_assigned_input_events() + _build_ui_list.call_deferred() + if input_icon_mapper: + input_icon_mapper.joypad_device_changed.connect(_refresh_ui_list_button_content) diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_actions_list.gd.uid b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_actions_list.gd.uid new file mode 100644 index 0000000..920d4f8 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_actions_list.gd.uid @@ -0,0 +1 @@ +uid://b3q5fgjev8gyo diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_actions_list.tscn b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_actions_list.tscn new file mode 100644 index 0000000..a582255 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_actions_list.tscn @@ -0,0 +1,44 @@ +[gd_scene load_steps=2 format=3 uid="uid://bxp45814v6ydv"] + +[ext_resource type="Script" uid="uid://b3q5fgjev8gyo" path="res://addons/maaacks_game_template/base/nodes/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 + +[node name="ActionNameLabel" type="Label" parent="ParentBoxContainer/ActionBoxContainer"] +custom_minimum_size = Vector2(150, 0) +layout_mode = 2 diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_actions_tree.gd b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_actions_tree.gd new file mode 100644 index 0000000..fa91297 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_actions_tree.gd @@ -0,0 +1,232 @@ +@tool +class_name InputActionsTree +extends Tree +## Scene to list the input actions out in a tree format. + +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) + +## The names of the action names that should be listed for editing. +@export var input_action_names : Array[StringName] : + set(value): + var _value_changed = input_action_names != value + input_action_names = value + if _value_changed: + _refresh_readable_action_names() +## The readable names of the action names that should be listed for editing. +@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 +## If true, capitalizes action names in order to make them readable. +@export var capitalize_action_names : bool = true : + set(value): + capitalize_action_names = value + _refresh_readable_action_names() +## Show action names that are not explicitely listed in an action name map. +@export var show_all_actions : bool = true +@export_group("Icons") +## Icon for the button that adds a new input to an action name. +@export var add_button_texture : Texture2D +## Icon for the button that removes an input to an action name. +@export var remove_button_texture : Texture2D +## Optional link to an input icon mapper to replace the text with icons. +@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 _refresh_readable_action_names(): + var _new_readable_action_names : Array[String] + for action_name in input_action_names: + if capitalize_action_names: + action_name = action_name.capitalize() + _new_readable_action_names.append(action_name) + readable_action_names = _new_readable_action_names + +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(action_name : StringName) -> String: + var readable_name : String + if action_name in action_name_map: + readable_name = action_name_map[action_name] + elif action_name in built_in_action_name_map: + readable_name = built_in_action_name_map[action_name] + else: + readable_name = action_name + if capitalize_action_names: + readable_name = readable_name.capitalize() + action_name_map[action_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.call_deferred() + 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) diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_actions_tree.gd.uid b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_actions_tree.gd.uid new file mode 100644 index 0000000..d09dd25 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_actions_tree.gd.uid @@ -0,0 +1 @@ +uid://bp7d2e5djo2tp diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_actions_tree.tscn b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_actions_tree.tscn new file mode 100644 index 0000000..48ab4b2 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_actions_tree.tscn @@ -0,0 +1,30 @@ +[gd_scene load_steps=4 format=3 uid="uid://ci6wgl2ngd35n"] + +[ext_resource type="Script" uid="uid://bp7d2e5djo2tp" path="res://addons/maaacks_game_template/base/nodes/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) +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 +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" +} diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_icon_mapper.gd b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_icon_mapper.gd new file mode 100644 index 0000000..d186536 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_icon_mapper.gd @@ -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() diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_icon_mapper.gd.uid b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_icon_mapper.gd.uid new file mode 100644 index 0000000..4a8d67b --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_icon_mapper.gd.uid @@ -0,0 +1 @@ +uid://cqigj1uumknrp diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_icon_mapper.tscn b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_icon_mapper.tscn new file mode 100644 index 0000000..c4e1585 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_icon_mapper.tscn @@ -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/nodes/menus/options_menu/input/input_icon_mapper.gd" id="1_msrpt"] + +[node name="InputIconMapper" type="Node"] +script = ExtResource("1_msrpt") diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_options_menu.gd b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_options_menu.gd new file mode 100644 index 0000000..5c650c9 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_options_menu.gd @@ -0,0 +1,92 @@ +@tool +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 = $KeyAssignmentWindow.text + +var last_input_readable_name + +func _ready() -> void: + remapping_mode = remapping_mode + +func _add_action_event() -> void: + var last_input_event = $KeyAssignmentWindow.last_input_event + last_input_readable_name = $KeyAssignmentWindow.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: + $ResetConfirmation.show() + +func _on_key_deletion_confirmation_confirmed() -> void: + var editing_item = %InputActionsTree.editing_item + if is_instance_valid(editing_item): + _remove_action_event(editing_item) + +func _on_key_assignment_window_confirmed() -> void: + _add_action_event() + +func _open_key_assignment_window(action_name : String, readable_input_name : String = assignment_placeholder_text) -> void: + $KeyAssignmentWindow.title = tr("Assign Key for {action}").format({action = action_name}) + $KeyAssignmentWindow.text = readable_input_name + $KeyAssignmentWindow.confirm_button.disabled = true + $KeyAssignmentWindow.show() + +func _on_input_actions_tree_add_button_clicked(action_name) -> void: + _open_key_assignment_window(action_name) + +func _on_input_actions_tree_remove_button_clicked(action_name, input_name) -> void: + $KeyDeletionConfirmation.title = tr("Remove Key for {action}").format({action = action_name}) + $KeyDeletionConfirmation.text = tr(KEY_DELETION_TEXT).format({key = input_name, action = action_name}) + $KeyDeletionConfirmation.show() + +func _popup_already_assigned(action_name, input_name) -> void: + $AlreadyAssignedMessage.text = tr(ALREADY_ASSIGNED_TEXT).format({key = input_name, action = action_name}) + $AlreadyAssignedMessage.show() + +func _popup_minimum_reached(action_name : String) -> void: + $OneInputMinimumMessage.text = ONE_INPUT_MINIMUM_TEXT % action_name + $OneInputMinimumMessage.show() + +func _on_input_actions_tree_already_assigned(action_name, input_name) -> void: + _popup_already_assigned.call_deferred(action_name, input_name) + +func _on_input_actions_tree_minimum_reached(action_name) -> void: + _popup_minimum_reached.call_deferred(action_name) + +func _on_input_actions_list_already_assigned(action_name, input_name) -> void: + _popup_already_assigned.call_deferred(action_name, input_name) + +func _on_input_actions_list_minimum_reached(action_name) -> void: + _popup_minimum_reached.call_deferred(action_name) + +func _on_input_actions_list_button_clicked(action_name, readable_input_name) -> void: + _open_key_assignment_window(action_name, readable_input_name) + +func _on_reset_confirmation_confirmed() -> void: + match(remapping_mode): + 0: + %InputActionsList.reset() + 1: + %InputActionsTree.reset() diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_options_menu.gd.uid b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_options_menu.gd.uid new file mode 100644 index 0000000..72cfa78 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_options_menu.gd.uid @@ -0,0 +1 @@ +uid://eborw7q4b07h diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_options_menu.tscn b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_options_menu.tscn new file mode 100644 index 0000000..7dac8d9 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_options_menu.tscn @@ -0,0 +1,103 @@ +[gd_scene load_steps=8 format=3 uid="uid://dp3rgqaehb3xu"] + +[ext_resource type="Script" uid="uid://eborw7q4b07h" path="res://addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_options_menu.gd" id="1"] +[ext_resource type="Script" uid="uid://1nf36h0gms3q" path="res://addons/maaacks_game_template/base/nodes/utilities/capture_focus.gd" id="2_wft4x"] +[ext_resource type="PackedScene" uid="uid://bxp45814v6ydv" path="res://addons/maaacks_game_template/base/nodes/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/nodes/menus/options_menu/input/input_actions_tree.tscn" id="5_b2whh"] +[ext_resource type="PackedScene" uid="uid://cwt4p3bufkke5" path="res://addons/maaacks_game_template/base/nodes/windows/confirmation_overlaid_window.tscn" id="7_5j1ya"] +[ext_resource type="PackedScene" uid="uid://dgravx3vt5g3i" path="res://addons/maaacks_game_template/base/nodes/menus/options_menu/input/key_assignment_window.tscn" id="7_r3r3g"] +[ext_resource type="PackedScene" uid="uid://6gdbfi0172ji" path="res://addons/maaacks_game_template/base/nodes/windows/overlaid_window.tscn" id="8_jtpjy"] + +[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="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 +alignment = 1 + +[node name="Label" type="Label" parent="VBoxContainer/InputMappingContainer"] +layout_mode = 2 +text = "Actions & Inputs" +horizontal_alignment = 1 + +[node name="InputActionsList" parent="VBoxContainer/InputMappingContainer" instance=ExtResource("4_lf2nw")] +unique_name_in_owner = true +custom_minimum_size = Vector2(560, 440) +layout_mode = 2 + +[node name="InputActionsTree" parent="VBoxContainer/InputMappingContainer" instance=ExtResource("5_b2whh")] +unique_name_in_owner = true +visible = false +custom_minimum_size = Vector2(400, 440) +layout_mode = 2 + +[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="KeyDeletionConfirmation" parent="." instance=ExtResource("7_5j1ya")] +visible = false +custom_minimum_size = Vector2(420, 200) +layout_mode = 2 +text = "Are you sure you want to remove KEY from ACTION?" +title = "Remove Key" + +[node name="ResetConfirmation" parent="." instance=ExtResource("7_5j1ya")] +visible = false +custom_minimum_size = Vector2(420, 200) +layout_mode = 2 +text = "Are you sure you want to reset controls back to the defaults?" +title = "Reset to Default" + +[node name="OneInputMinimumMessage" parent="." instance=ExtResource("8_jtpjy")] +visible = false +custom_minimum_size = Vector2(420, 200) +layout_mode = 2 +update_content = true +title = "One Input Minimum" + +[node name="AlreadyAssignedMessage" parent="." instance=ExtResource("8_jtpjy")] +visible = false +custom_minimum_size = Vector2(420, 200) +layout_mode = 2 +update_content = true +title = "Already Assigned" + +[node name="KeyAssignmentWindow" parent="." instance=ExtResource("7_r3r3g")] +visible = false +layout_mode = 2 + +[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="KeyDeletionConfirmation" to="." method="_on_key_deletion_confirmation_confirmed"] +[connection signal="confirmed" from="ResetConfirmation" to="." method="_on_reset_confirmation_confirmed"] +[connection signal="confirmed" from="KeyAssignmentWindow" to="." method="_on_key_assignment_window_confirmed"] diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/input/key_assignment_window.gd b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/key_assignment_window.gd new file mode 100644 index 0000000..bc0cca9 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/key_assignment_window.gd @@ -0,0 +1,112 @@ +@tool +extends ConfirmationOverlaidWindow +## Scene to confirm a new input for an action name. + +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 +} +## Confirmations required before a new input is accepted for an aciton. +@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 + confirm_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_input_text_edit_focus_entered() -> void: + _start_listening.call_deferred() + +func _on_input_text_edit_focus_exited() -> void: + _stop_listening() + +func _focus_on_ok() -> void: + confirm_button.grab_focus() + +func _ready() -> void: + confirm_button.focus_neighbor_top = ^"../../../BodyMargin/VBoxContainer/InputTextEdit" + close_button.focus_neighbor_top = ^"../../../BodyMargin/VBoxContainer/InputTextEdit" + super._ready() + +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() + close() + +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: + super._on_visibility_changed() + if visible: + if not text.strip_edges().is_empty(): + %InputLabel.text = text + else: + %InputLabel.text = NO_INPUT_TEXT + %InputTextEdit.grab_focus() diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/input/key_assignment_window.gd.uid b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/key_assignment_window.gd.uid new file mode 100644 index 0000000..b34806b --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/key_assignment_window.gd.uid @@ -0,0 +1 @@ +uid://custha7r0uoic diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/input/key_assignment_window.tscn b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/key_assignment_window.tscn new file mode 100644 index 0000000..8a40e15 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/input/key_assignment_window.tscn @@ -0,0 +1,65 @@ +[gd_scene load_steps=3 format=3 uid="uid://dgravx3vt5g3i"] + +[ext_resource type="PackedScene" uid="uid://cwt4p3bufkke5" path="res://addons/maaacks_game_template/base/nodes/windows/confirmation_overlaid_window.tscn" id="1_6c67a"] +[ext_resource type="Script" uid="uid://custha7r0uoic" path="res://addons/maaacks_game_template/base/nodes/menus/options_menu/input/key_assignment_window.gd" id="2_oif0q"] + +[node name="KeyAssignmentWindow" instance=ExtResource("1_6c67a")] +custom_minimum_size = Vector2(420, 200) +offset_left = -210.0 +offset_top = -100.0 +offset_right = 210.0 +offset_bottom = 100.0 +script = ExtResource("2_oif0q") +input_confirmation = 0 +close_button_text = "Close" +title = "Set Input" + +[node name="TitleLabel" parent="ContentContainer/BoxContainer/TitleMargin/BoxContainer" index="0"] +text = "Set Input" + +[node name="DescriptionLabel" parent="ContentContainer/BoxContainer/BodyMargin" index="0"] +visible = false + +[node name="VBoxContainer" type="VBoxContainer" parent="ContentContainer/BoxContainer/BodyMargin" index="1"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="InputLabel" type="Label" parent="ContentContainer/BoxContainer/BodyMargin/VBoxContainer" index="0"] +unique_name_in_owner = true +layout_mode = 2 +text = "None" +horizontal_alignment = 1 + +[node name="InputTextEdit" type="TextEdit" parent="ContentContainer/BoxContainer/BodyMargin/VBoxContainer" index="1"] +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="MenuButtons" parent="ContentContainer/BoxContainer/MenuButtonsMargin" index="0"] +null_focus_enabled = false +joypad_enabled = false +mouse_hidden_enabled = false + +[node name="CloseButton" parent="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="0"] +focus_neighbor_top = NodePath("../../../BodyMargin/VBoxContainer/InputTextEdit") +text = "Close" + +[node name="ConfirmButton" parent="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="1"] +focus_neighbor_top = NodePath("../../../BodyMargin/VBoxContainer/InputTextEdit") + +[node name="DelayTimer" type="Timer" parent="." index="1"] +unique_name_in_owner = true +wait_time = 0.1 +one_shot = true + +[connection signal="focus_entered" from="ContentContainer/BoxContainer/BodyMargin/VBoxContainer/InputTextEdit" to="." method="_on_input_text_edit_focus_entered"] +[connection signal="focus_exited" from="ContentContainer/BoxContainer/BodyMargin/VBoxContainer/InputTextEdit" to="." method="_on_input_text_edit_focus_exited"] +[connection signal="gui_input" from="ContentContainer/BoxContainer/BodyMargin/VBoxContainer/InputTextEdit" to="." method="_on_input_text_edit_gui_input"] diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/mini_options_menu.gd b/addons/maaacks_game_template/base/nodes/menus/options_menu/mini_options_menu.gd new file mode 100644 index 0000000..c47d5df --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/mini_options_menu.gd @@ -0,0 +1,46 @@ +extends Control + +@onready var mute_control = %MuteControl +@onready var fullscreen_control = %FullscreenControl + +## Scene for adjusting the volume of the audio busses. +@export var audio_control_scene : PackedScene +## Optional names of audio busses that should be ignored. +@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()) diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/mini_options_menu.gd.uid b/addons/maaacks_game_template/base/nodes/menus/options_menu/mini_options_menu.gd.uid new file mode 100644 index 0000000..926a13c --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/mini_options_menu.gd.uid @@ -0,0 +1 @@ +uid://c0jjk82iuuyh3 diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/list_option_control.gd b/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/list_option_control.gd new file mode 100644 index 0000000..c6a9b7a --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/list_option_control.gd @@ -0,0 +1,84 @@ +@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 _refresh_option_values(value : Variant) -> void: + if option_values.is_empty(): return + if value == null: + return + 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)) + +func set_value(value : Variant) -> void: + _refresh_option_values(value) + value = custom_option_values.find(value) + 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() diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/list_option_control.gd.uid b/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/list_option_control.gd.uid new file mode 100644 index 0000000..69ab4eb --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/list_option_control.gd.uid @@ -0,0 +1 @@ +uid://b8xqufg4re3c2 diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/list_option_control.tscn b/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/list_option_control.tscn new file mode 100644 index 0000000..70834aa --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/list_option_control.tscn @@ -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/nodes/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/nodes/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 diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/option_control.gd b/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/option_control.gd new file mode 100644 index 0000000..231ee21 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/option_control.gd @@ -0,0 +1,136 @@ +@tool +class_name OptionControl +extends Control +## Generic scene for editing a value of the [PlayerConfig]. + +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 + PlayerConfig.set_config(section, key, value) + setting_changed.emit(value) + +func _get_setting(default : Variant = null) -> Variant: + return PlayerConfig.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) -> void: + 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 + +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} + ] diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/option_control.gd.uid b/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/option_control.gd.uid new file mode 100644 index 0000000..54d3316 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/option_control.gd.uid @@ -0,0 +1 @@ +uid://cafqki2b08kwu diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/option_control.tscn b/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/option_control.tscn new file mode 100644 index 0000000..cdb50cc --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/option_control.tscn @@ -0,0 +1,17 @@ +[gd_scene load_steps=2 format=3 uid="uid://d7te75il06t7"] + +[ext_resource type="Script" uid="uid://cafqki2b08kwu" path="res://addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/option_control.gd" id="1_jvl5q"] + +[node name="OptionControl" type="HBoxContainer"] +custom_minimum_size = Vector2(0, 40) +offset_right = 400.0 +offset_bottom = 40.0 +script = ExtResource("1_jvl5q") +default_value = false + +[node name="OptionLabel" type="Label" parent="."] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +text = " :" +vertical_alignment = 1 diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/slider_option_control.tscn b/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/slider_option_control.tscn new file mode 100644 index 0000000..b555dd9 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/slider_option_control.tscn @@ -0,0 +1,19 @@ +[gd_scene load_steps=2 format=3 uid="uid://cl416gdb1fgwr"] + +[ext_resource type="PackedScene" uid="uid://d7te75il06t7" path="res://addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/option_control.tscn" id="1_16hlr"] + +[node name="OptionControl" instance=ExtResource("1_16hlr")] +custom_minimum_size = Vector2(0, 28) +offset_bottom = 28.0 +property_type = 3 +default_value = 1.0 + +[node name="HSlider" type="HSlider" parent="." index="1"] +custom_minimum_size = Vector2(256, 0) +layout_mode = 2 +size_flags_vertical = 4 +max_value = 1.0 +step = 0.05 +value = 1.0 +tick_count = 11 +ticks_on_borders = true diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/toggle_option_control.tscn b/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/toggle_option_control.tscn new file mode 100644 index 0000000..59c098d --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/toggle_option_control.tscn @@ -0,0 +1,8 @@ +[gd_scene load_steps=2 format=3 uid="uid://bsxh6v7j0257h"] + +[ext_resource type="PackedScene" uid="uid://d7te75il06t7" path="res://addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/option_control.tscn" id="1_8rnmo"] + +[node name="OptionControl" instance=ExtResource("1_8rnmo")] + +[node name="CheckButton" type="CheckButton" parent="." index="1"] +layout_mode = 2 diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/vector_2_list_option_control.gd b/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/vector_2_list_option_control.gd new file mode 100644 index 0000000..562d2e3 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/vector_2_list_option_control.gd @@ -0,0 +1,9 @@ +@tool +class_name Vector2ListOptionControl +extends ListOptionControl + +func _value_title_map(value : Variant) -> String: + if value is Vector2 or value is Vector2i: + return "%d x %d" % [value.x , value.y] + else: + return super._value_title_map(value) diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/vector_2_list_option_control.gd.uid b/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/vector_2_list_option_control.gd.uid new file mode 100644 index 0000000..562da78 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/vector_2_list_option_control.gd.uid @@ -0,0 +1 @@ +uid://brntdgf3sv0s0 diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/vector_2_list_option_control.tscn b/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/vector_2_list_option_control.tscn new file mode 100644 index 0000000..b94b560 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/vector_2_list_option_control.tscn @@ -0,0 +1,7 @@ +[gd_scene load_steps=3 format=3 uid="uid://c01ayjblhcg1t"] + +[ext_resource type="PackedScene" uid="uid://b6bl3n5mp3m1e" path="res://addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/list_option_control.tscn" id="1_jqwiw"] +[ext_resource type="Script" uid="uid://brntdgf3sv0s0" path="res://addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/vector_2_list_option_control.gd" id="2_w33vs"] + +[node name="OptionControl" instance=ExtResource("1_jqwiw")] +script = ExtResource("2_w33vs") diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/paginated_tab_container.gd b/addons/maaacks_game_template/base/nodes/menus/options_menu/paginated_tab_container.gd new file mode 100644 index 0000000..ace973b --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/paginated_tab_container.gd @@ -0,0 +1,13 @@ +extends TabContainer +## Applies UI page up and page down inputs to tab switching. + +func _unhandled_input(event : InputEvent) -> void: + if not is_visible_in_tree(): + return + if event.is_action_pressed("ui_page_down"): + current_tab = (current_tab+1) % get_tab_count() + elif event.is_action_pressed("ui_page_up"): + if current_tab == 0: + current_tab = get_tab_count()-1 + else: + current_tab = current_tab-1 diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/paginated_tab_container.gd.uid b/addons/maaacks_game_template/base/nodes/menus/options_menu/paginated_tab_container.gd.uid new file mode 100644 index 0000000..01c0cbd --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/paginated_tab_container.gd.uid @@ -0,0 +1 @@ +uid://c3mignmhuvvq4 diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/video/video_options_menu.gd b/addons/maaacks_game_template/base/nodes/menus/options_menu/video/video_options_menu.gd new file mode 100644 index 0000000..298d53b --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/video/video_options_menu.gd @@ -0,0 +1,37 @@ +extends Control + +func _preselect_resolution(window : Window) -> void: + %ResolutionControl.value = window.size + +func _update_resolution_options_enabled(window : Window) -> void: + if OS.has_feature("web"): + %ResolutionControl.editable = false + %ResolutionControl.tooltip_text = "Disabled for web" + elif AppSettings.is_fullscreen(window): + %ResolutionControl.editable = false + %ResolutionControl.tooltip_text = "Disabled for fullscreen" + else: + %ResolutionControl.editable = true + %ResolutionControl.tooltip_text = "Select a screen size" + +func _update_ui(window : Window) -> void: + %FullscreenControl.value = AppSettings.is_fullscreen(window) + _preselect_resolution(window) + %VSyncControl.value = AppSettings.get_vsync(window) + _update_resolution_options_enabled(window) + +func _ready() -> void: + var window : Window = get_window() + _update_ui(window) + window.connect("size_changed", _preselect_resolution.bind(window)) + +func _on_fullscreen_control_setting_changed(value) -> void: + var window : Window = get_window() + AppSettings.set_fullscreen_enabled(value, window) + _update_resolution_options_enabled(window) + +func _on_resolution_control_setting_changed(value) -> void: + AppSettings.set_resolution(value, get_window(), false) + +func _on_v_sync_control_setting_changed(value) -> void: + AppSettings.set_vsync(value, get_window()) diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/video/video_options_menu.gd.uid b/addons/maaacks_game_template/base/nodes/menus/options_menu/video/video_options_menu.gd.uid new file mode 100644 index 0000000..77495f0 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/video/video_options_menu.gd.uid @@ -0,0 +1 @@ +uid://cpe5r24151r5n diff --git a/addons/maaacks_game_template/base/nodes/menus/options_menu/video/video_options_menu.tscn b/addons/maaacks_game_template/base/nodes/menus/options_menu/video/video_options_menu.tscn new file mode 100644 index 0000000..dfe3666 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/menus/options_menu/video/video_options_menu.tscn @@ -0,0 +1,60 @@ +[gd_scene load_steps=6 format=3 uid="uid://b2numvphf2kau"] + +[ext_resource type="Script" uid="uid://cpe5r24151r5n" path="res://addons/maaacks_game_template/base/nodes/menus/options_menu/video/video_options_menu.gd" id="1"] +[ext_resource type="Script" uid="uid://1nf36h0gms3q" path="res://addons/maaacks_game_template/base/nodes/utilities/capture_focus.gd" id="2_dgrai"] +[ext_resource type="PackedScene" uid="uid://bsxh6v7j0257h" path="res://addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/toggle_option_control.tscn" id="3_uded6"] +[ext_resource type="PackedScene" uid="uid://c01ayjblhcg1t" path="res://addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/vector_2_list_option_control.tscn" id="4_gwtfq"] +[ext_resource type="PackedScene" uid="uid://b6bl3n5mp3m1e" path="res://addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/list_option_control.tscn" id="5_881de"] + +[node name="Video" type="MarginContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +theme_override_constants/margin_top = 24 +theme_override_constants/margin_bottom = 24 +script = ExtResource("1") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +custom_minimum_size = Vector2(400, 0) +layout_mode = 2 +size_flags_horizontal = 4 +alignment = 1 +script = ExtResource("2_dgrai") +search_depth = 2 + +[node name="FullscreenControl" parent="VBoxContainer" instance=ExtResource("3_uded6")] +unique_name_in_owner = true +layout_mode = 2 +option_name = "Fullscreen" +option_section = 3 +key = "Fullscreen" +section = "VideoSettings" + +[node name="ResolutionControl" parent="VBoxContainer" instance=ExtResource("4_gwtfq")] +unique_name_in_owner = true +layout_mode = 2 +tooltip_text = "Select a screen size" +option_values = [Vector2i(640, 360), Vector2i(960, 540), Vector2i(1024, 576), Vector2i(1280, 720), Vector2i(1600, 900), Vector2i(1920, 1080), Vector2i(2048, 1152), Vector2i(2560, 1440), Vector2i(3200, 1800), Vector2i(3840, 2160)] +option_titles = Array[String](["640 x 360", "960 x 540", "1024 x 576", "1280 x 720", "1600 x 900", "1920 x 1080", "2048 x 1152", "2560 x 1440", "3200 x 1800", "3840 x 2160"]) +option_name = "Resolution" +option_section = 3 +key = "ScreenResolution" +section = "VideoSettings" +property_type = 6 + +[node name="VSyncControl" parent="VBoxContainer" instance=ExtResource("5_881de")] +unique_name_in_owner = true +layout_mode = 2 +lock_titles = true +option_values = [0, 1, 2, 3] +option_titles = Array[String](["Disabled", "Enabled", "Adaptive", "Mailbox"]) +option_name = "V-Sync" +option_section = 3 +key = "V-Sync" +section = "VideoSettings" +property_type = 2 +default_value = 0 + +[connection signal="setting_changed" from="VBoxContainer/FullscreenControl" to="." method="_on_fullscreen_control_setting_changed"] +[connection signal="setting_changed" from="VBoxContainer/ResolutionControl" to="." method="_on_resolution_control_setting_changed"] +[connection signal="setting_changed" from="VBoxContainer/VSyncControl" to="." method="_on_v_sync_control_setting_changed"] diff --git a/addons/maaacks_game_template/base/nodes/opening/opening.gd b/addons/maaacks_game_template/base/nodes/opening/opening.gd new file mode 100644 index 0000000..c10847a --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/opening/opening.gd @@ -0,0 +1,126 @@ +extends Control +## Scene for displaying opening logos, placards, or other images before a game. + +## Defines the path to the next scene. +@export_file("*.tscn") var next_scene_path : String +## The list of images to show in the opening sequence. +@export var images : Array[Texture2D] +@export_group("Animation") +## The time to fade-in the next image. +@export var fade_in_time : float = 0.2 +## The time to fade-out the previous image. +@export var fade_out_time : float = 0.2 +## The time to keep an image visible after fade-in and before fade-out. +@export var visible_time : float = 1.6 +@export_group("Transition") +## The delay before starting the first fade-in animation once ready. +@export var start_delay : float = 0.5 +## The delay after ending the last fade-in animation before loading the next scene. +@export var end_delay : float = 0.5 +## If true, show a loading screen if the next scene is not yet ready. +## Requires Maaack's Scene Loader. +@export var show_loading_screen : bool = false + +## If Maaack's Scene Loader is installed, then it will be used to change scenes. +@onready var scene_loader_node = get_tree().root.get_node_or_null(^"SceneLoader") + +var tween : Tween +var next_image_index : int = 0 + +func get_next_scene_path() -> String: + return next_scene_path + +func _on_scene_loaded() -> void: + scene_loader_node.change_scene_to_resource() + +func _load_next_scene() -> void: + if scene_loader_node: + var status = scene_loader_node.get_status() + if status == ResourceLoader.THREAD_LOAD_LOADED: + _on_scene_loaded() + elif show_loading_screen: + scene_loader_node.change_scene_to_loading_screen() + elif not scene_loader_node.scene_loaded.is_connected(_on_scene_loaded): + scene_loader_node.scene_loaded.connect(_on_scene_loaded, CONNECT_ONE_SHOT) + else: + get_tree().change_scene_to_file(get_next_scene_path()) + +func _add_textures_to_container(textures : Array[Texture2D]) -> void: + for texture in textures: + var texture_rect : TextureRect = TextureRect.new() + texture_rect.texture = texture + texture_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED + texture_rect.expand_mode = TextureRect.EXPAND_IGNORE_SIZE + texture_rect.modulate.a = 0.0 + %ImagesContainer.call_deferred("add_child", texture_rect) + +func _event_skips_image(event : InputEvent) -> bool: + return event.is_action_released(&"ui_accept") or event.is_action_released(&"ui_select") + +func _event_skips_intro(event : InputEvent) -> bool: + return event.is_action_released(&"ui_cancel") + +func _event_is_mouse_button_released(event : InputEvent) -> bool: + return event is InputEventMouseButton and not event.is_pressed() + +func _unhandled_input(event : InputEvent) -> void: + if _event_skips_intro(event): + _load_next_scene() + elif _event_skips_image(event): + _show_next_image(false) + +func _gui_input(event : InputEvent) -> void: + if _event_is_mouse_button_released(event): + _show_next_image(false) + +func _transition_out() -> void: + await get_tree().create_timer(end_delay).timeout + _load_next_scene() + +func _transition_in() -> void: + await get_tree().create_timer(start_delay).timeout + if next_image_index == 0: + _show_next_image() + +func _wait_and_fade_out(texture_rect : TextureRect) -> void: + var _compare_next_index = next_image_index + await get_tree().create_timer(visible_time, false).timeout + if _compare_next_index != next_image_index : return + tween = create_tween() + tween.tween_property(texture_rect, "modulate:a", 0.0, fade_out_time) + await tween.finished + _show_next_image.call_deferred() + +func _hide_previous_image() -> void: + if tween and tween.is_running(): + tween.stop() + if %ImagesContainer.get_child_count() == 0: + return + var current_image = %ImagesContainer.get_child(next_image_index - 1) + if current_image: + current_image.modulate.a = 0.0 + +func _show_next_image(animated : bool = true) -> void: + _hide_previous_image() + if next_image_index >= %ImagesContainer.get_child_count(): + if animated: + _transition_out() + else: + _load_next_scene() + return + var texture_rect = %ImagesContainer.get_child(next_image_index) + if animated: + tween = create_tween() + tween.tween_property(texture_rect, "modulate:a", 1.0, fade_in_time) + await tween.finished + else: + texture_rect.modulate.a = 1.0 + next_image_index += 1 + _wait_and_fade_out(texture_rect) + +func _ready() -> void: + AppSettings.set_from_config_and_window(get_window()) + if scene_loader_node: + scene_loader_node.load_scene(get_next_scene_path(), true) + _add_textures_to_container(images) + _transition_in() diff --git a/addons/maaacks_game_template/base/nodes/opening/opening.gd.uid b/addons/maaacks_game_template/base/nodes/opening/opening.gd.uid new file mode 100644 index 0000000..4b25152 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/opening/opening.gd.uid @@ -0,0 +1 @@ +uid://dtco0s8byckx6 diff --git a/addons/maaacks_game_template/base/nodes/opening/opening.tscn b/addons/maaacks_game_template/base/nodes/opening/opening.tscn new file mode 100644 index 0000000..522a6fa --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/opening/opening.tscn @@ -0,0 +1,21 @@ +[gd_scene load_steps=2 format=3 uid="uid://sikc02ddepyt"] + +[ext_resource type="Script" uid="uid://dtco0s8byckx6" path="res://addons/maaacks_game_template/base/nodes/opening/opening.gd" id="1_fcjph"] + +[node name="Opening" 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_fcjph") + +[node name="ImagesContainer" 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 diff --git a/addons/maaacks_game_template/base/nodes/utilities/capture_focus.gd b/addons/maaacks_game_template/base/nodes/utilities/capture_focus.gd new file mode 100644 index 0000000..e6d41ef --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/utilities/capture_focus.gd @@ -0,0 +1,68 @@ +extends Control +## Node that captures UI focus when switching menus. +## +## This script assists with capturing UI focus when +## opening, closing, or switching between menus. +## When attached to a node, it will check if it was changed to visible +## and if it should grab focus. If both are true, it will capture focus +## on the first eligible node in its scene tree. + +## Hierarchical depth to search in the scene tree for a focusable control node. +@export var search_depth : int = 1 +## If true, always capture focus when made visible. +@export var enabled : bool = false +## If true, capture focus if nothing currently is in focus. +@export var null_focus_enabled : bool = true +## If true, capture focus if there is a joypad detected. +@export var joypad_enabled : bool = true +## If true, capture focus if the mouse is hidden. +@export var mouse_hidden_enabled : bool = true + +## Locks focus +@export var lock : bool = false : + set(value): + var value_changed : bool = lock != value + lock = value + if value_changed and not lock: + update_focus() + +func _focus_first_search(control_node : Control, levels : int = 1) -> bool: + if control_node == null or !control_node.is_visible_in_tree(): + return false + if control_node.focus_mode == FOCUS_ALL: + control_node.grab_focus() + if control_node is ItemList: + control_node.select(0) + return true + if levels < 1: + return false + var children = control_node.get_children() + for child in children: + if _focus_first_search(child, levels - 1): + return true + return false + +func focus_first() -> void: + _focus_first_search(self, search_depth) + +func update_focus() -> void: + if lock : return + if _is_visible_and_should_capture(): + focus_first() + +func _should_capture_focus() -> bool: + return enabled or \ + (get_viewport().gui_get_focus_owner() == null and null_focus_enabled) or \ + (Input.get_connected_joypads().size() > 0 and joypad_enabled) or \ + (Input.mouse_mode not in [Input.MOUSE_MODE_VISIBLE, Input.MOUSE_MODE_CONFINED] and mouse_hidden_enabled) + +func _is_visible_and_should_capture() -> bool: + return is_visible_in_tree() and _should_capture_focus() + +func _on_visibility_changed() -> void: + call_deferred("update_focus") + +func _ready() -> void: + if is_inside_tree(): + update_focus() + connect("visibility_changed", _on_visibility_changed) diff --git a/addons/maaacks_game_template/base/nodes/utilities/capture_focus.gd.uid b/addons/maaacks_game_template/base/nodes/utilities/capture_focus.gd.uid new file mode 100644 index 0000000..b0eccab --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/utilities/capture_focus.gd.uid @@ -0,0 +1 @@ +uid://1nf36h0gms3q diff --git a/addons/maaacks_game_template/base/nodes/utilities/file_lister.gd b/addons/maaacks_game_template/base/nodes/utilities/file_lister.gd new file mode 100644 index 0000000..8c1c9e4 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/utilities/file_lister.gd @@ -0,0 +1,55 @@ +@tool +extends Node +class_name FileLister +## Helper class for listing all the scenes in a directory. + +## List of paths to scene files. +@export var _refresh_files_action : bool = false : + set(value): + if value and Engine.is_editor_hint(): + _refresh_files() +# For Godot 4.4 +# @export_tool_button("Refresh Files") var _refresh_files_action = _refresh_files +## Filled in the editor by selecting a directory. +@export var files : Array[String] +## Fills files with those discovered in directories, and matching constraints. +@export_dir var directories : Array[String] : + set(value): + directories = value + _refresh_files() +@export_group("Constraints") +## Include any results that match the string. +@export var search : String +## Exclude any results that match the string. +@export var filter : String +@export_subgroup("Advanced Search") +## Include any results that begin with the string. +@export var begins_with : String +## Include any results that end with the string. +@export var ends_with : String +## Exclude any results that begin with the string. +@export var not_begins_with : String +## Exclude any results that end with the string. +@export var not_ends_with : String + + +func _refresh_files(): + if not is_inside_tree(): return + files.clear() + for directory in directories: + var dir_access = DirAccess.open(directory) + if dir_access: + for file in dir_access.get_files(): + if (not search.is_empty()) and (not file.contains(search)): + continue + if (not filter.is_empty()) and (file.contains(filter)): + continue + if (not begins_with.is_empty()) and (not file.begins_with(begins_with)): + continue + if (not ends_with.is_empty()) and (not file.ends_with(ends_with)): + continue + if (not not_begins_with.is_empty()) and (file.begins_with(not_begins_with)): + continue + if (not not_ends_with.is_empty()) and (file.ends_with(not_ends_with)): + continue + files.append(directory + "/" + file) diff --git a/addons/maaacks_game_template/base/nodes/utilities/file_lister.gd.uid b/addons/maaacks_game_template/base/nodes/utilities/file_lister.gd.uid new file mode 100644 index 0000000..ae15737 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/utilities/file_lister.gd.uid @@ -0,0 +1 @@ +uid://bij7wsh8d44gv diff --git a/addons/maaacks_game_template/base/nodes/utilities/input_helper.gd b/addons/maaacks_game_template/base/nodes/utilities/input_helper.gd new file mode 100644 index 0000000..affab91 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/utilities/input_helper.gd @@ -0,0 +1,175 @@ +class_name InputEventHelper +extends Node +## Helper class for organizing constants related to [InputEvent]. + +const DEVICE_KEYBOARD = "Keyboard" +const DEVICE_MOUSE = "Mouse" +const DEVICE_XBOX_CONTROLLER = "Xbox" +const DEVICE_SWITCH_CONTROLLER = "Switch" +const DEVICE_SWITCH_JOYCON_LEFT_CONTROLLER = "Switch Left Joycon" +const DEVICE_SWITCH_JOYCON_RIGHT_CONTROLLER = "Switch Right Joycon" +const DEVICE_SWITCH_JOYCON_COMBINED_CONTROLLER = "Switch Combined Joycons" +const DEVICE_PLAYSTATION_CONTROLLER = "Playstation" +const DEVICE_STEAMDECK_CONTROLLER = "Steamdeck" +const DEVICE_GENERIC = "Generic" + +const JOYSTICK_LEFT_NAME = "Left Stick" +const JOYSTICK_RIGHT_NAME = "Right Stick" +const D_PAD_NAME = "Dpad" + +const MOUSE_BUTTONS : Array = ["None", "Left", "Right", "Middle", "Scroll Up", "Scroll Down", "Wheel Left", "Wheel Right"] + +const JOYPAD_BUTTON_NAME_MAP : Dictionary = { + DEVICE_GENERIC : ["Trigger A", "Trigger B", "Trigger C", "", "", "", "", "Left Stick Press", "Right Stick Press", "Left Shoulder", "Right Shoulder", "Up", "Down", "Left", "Right"], + DEVICE_XBOX_CONTROLLER : ["A", "B", "X", "Y", "View", "Home", "Menu", "Left Stick Press", "Right Stick Press", "Left Shoulder", "Right Shoulder", "Up", "Down", "Left", "Right", "Share"], + DEVICE_SWITCH_CONTROLLER : ["B", "A", "Y", "X", "Minus", "", "Plus", "Left Stick Press", "Right Stick Press", "Left Shoulder", "Right Shoulder", "Up", "Down", "Left", "Right", "Capture"], + DEVICE_PLAYSTATION_CONTROLLER : ["Cross", "Circle", "Square", "Triangle", "Select", "PS", "Options", "Left Stick Press", "Right Stick Press", "Left Shoulder", "Right Shoulder", "Up", "Down", "Left", "Right", "Microphone"], + DEVICE_STEAMDECK_CONTROLLER : ["A", "B", "X", "Y", "View", "", "Options", "Left Stick Press", "Right Stick Press", "Left Shoulder", "Right Shoulder", "Up", "Down", "Left", "Right"] +} # Dictionary[String, Array] + +const SDL_DEVICE_NAMES: Dictionary = { + DEVICE_XBOX_CONTROLLER: ["XInput", "XBox"], + DEVICE_PLAYSTATION_CONTROLLER: ["Sony", "PS5", "PS4", "Nacon"], + DEVICE_STEAMDECK_CONTROLLER: ["Steam"], + DEVICE_SWITCH_CONTROLLER: ["Switch"], + DEVICE_SWITCH_JOYCON_LEFT_CONTROLLER: ["Joy-Con (L)", "Left Joy-Con"], + DEVICE_SWITCH_JOYCON_RIGHT_CONTROLLER: ["Joy-Con (R)", "Right Joy-Con"], + DEVICE_SWITCH_JOYCON_COMBINED_CONTROLLER: ["Joy-Con (L/R)", "Combined Joy-Cons"], +} + +const JOY_BUTTON_NAMES : Dictionary = { + JOY_BUTTON_A: "Button A", + JOY_BUTTON_B: "Button B", + JOY_BUTTON_X: "Button X", + JOY_BUTTON_Y: "Button Y", + JOY_BUTTON_LEFT_SHOULDER: "Left Shoulder", + JOY_BUTTON_RIGHT_SHOULDER: "Right Shoulder", + JOY_BUTTON_LEFT_STICK: "Left Stick", + JOY_BUTTON_RIGHT_STICK: "Right Stick", + JOY_BUTTON_START : "Button Start", + JOY_BUTTON_GUIDE : "Button Guide", + JOY_BUTTON_BACK : "Button Back", + JOY_BUTTON_DPAD_UP : D_PAD_NAME + " Up", + JOY_BUTTON_DPAD_DOWN : D_PAD_NAME + " Down", + JOY_BUTTON_DPAD_LEFT : D_PAD_NAME + " Left", + JOY_BUTTON_DPAD_RIGHT : D_PAD_NAME + " Right", + JOY_BUTTON_MISC1 : "Misc", +} + +const JOYPAD_DPAD_NAMES : Dictionary = { + JOY_BUTTON_DPAD_UP : D_PAD_NAME + " Up", + JOY_BUTTON_DPAD_DOWN : D_PAD_NAME + " Down", + JOY_BUTTON_DPAD_LEFT : D_PAD_NAME + " Left", + JOY_BUTTON_DPAD_RIGHT : D_PAD_NAME + " Right", +} + +const JOY_AXIS_NAMES : Dictionary = { + JOY_AXIS_TRIGGER_LEFT: "Left Trigger", + JOY_AXIS_TRIGGER_RIGHT: "Right Trigger", +} + +const BUILT_IN_ACTION_NAME_MAP : Dictionary = { + "ui_accept" : "Accept", + "ui_select" : "Select", + "ui_cancel" : "Cancel", + "ui_focus_next" : "Focus Next", + "ui_focus_prev" : "Focus Prev", + "ui_left" : "Left (UI)", + "ui_right" : "Right (UI)", + "ui_up" : "Up (UI)", + "ui_down" : "Down (UI)", + "ui_page_up" : "Page Up", + "ui_page_down" : "Page Down", + "ui_home" : "Home", + "ui_end" : "End", + "ui_cut" : "Cut", + "ui_copy" : "Copy", + "ui_paste" : "Paste", + "ui_undo" : "Undo", + "ui_redo" : "Redo", +} + +static func has_joypad() -> bool: + return Input.get_connected_joypads().size() > 0 + +static func is_joypad_event(event: InputEvent) -> bool: + return event is InputEventJoypadButton or event is InputEventJoypadMotion + +static func is_mouse_event(event: InputEvent) -> bool: + return event is InputEventMouseButton or event is InputEventMouseMotion + +static func get_device_name_by_id(device_id : int) -> String: + if device_id >= 0: + var device_name = Input.get_joy_name(device_id) + for device_key in SDL_DEVICE_NAMES: + for keyword in SDL_DEVICE_NAMES[device_key]: + if device_name.containsn(keyword): + return device_key + return DEVICE_GENERIC + +static func get_device_name(event: InputEvent) -> String: + if event is InputEventJoypadButton or event is InputEventJoypadMotion: + if event.device == -1: + return DEVICE_GENERIC + var device_id = event.device + return get_device_name_by_id(device_id) + return DEVICE_GENERIC + +static func _display_server_supports_keycode_from_physical(): + return OS.has_feature("windows") or OS.has_feature("macos") or OS.has_feature("linux") + +static func get_text(event : InputEvent) -> String: + if event == null: + return "" + if event is InputEventJoypadButton: + if event.button_index in JOY_BUTTON_NAMES: + return JOY_BUTTON_NAMES[event.button_index] + elif event is InputEventJoypadMotion: + var full_string := "" + var direction_string := "" + var is_right_or_down : bool = event.axis_value > 0.0 + if event.axis in JOY_AXIS_NAMES: + return JOY_AXIS_NAMES[event.axis] + match(event.axis): + JOY_AXIS_LEFT_X: + full_string = JOYSTICK_LEFT_NAME + direction_string = "Right" if is_right_or_down else "Left" + JOY_AXIS_LEFT_Y: + full_string = JOYSTICK_LEFT_NAME + direction_string = "Down" if is_right_or_down else "Up" + JOY_AXIS_RIGHT_X: + full_string = JOYSTICK_RIGHT_NAME + direction_string = "Right" if is_right_or_down else "Left" + JOY_AXIS_RIGHT_Y: + full_string = JOYSTICK_RIGHT_NAME + direction_string = "Down" if is_right_or_down else "Up" + full_string += " " + direction_string + return full_string + elif event is InputEventKey: + var keycode : Key = event.get_physical_keycode() + if keycode: + keycode = event.get_physical_keycode_with_modifiers() + else: + keycode = event.get_keycode_with_modifiers() + if _display_server_supports_keycode_from_physical(): + keycode = DisplayServer.keyboard_get_keycode_from_physical(keycode) + return OS.get_keycode_string(keycode) + return event.as_text() + +static func get_device_specific_text(event : InputEvent, device_name : String = "") -> String: + if device_name.is_empty(): + device_name = get_device_name(event) + if event is InputEventJoypadButton: + var joypad_button : String = "" + if event.button_index in JOYPAD_DPAD_NAMES: + joypad_button = JOYPAD_DPAD_NAMES[event.button_index] + elif event.button_index < JOYPAD_BUTTON_NAME_MAP[device_name].size(): + joypad_button = JOYPAD_BUTTON_NAME_MAP[device_name][event.button_index] + return "%s %s" % [device_name, joypad_button] + if event is InputEventJoypadMotion: + return "%s %s" % [device_name, get_text(event)] + if event is InputEventMouseButton: + if event.button_index < MOUSE_BUTTONS.size(): + var mouse_button : String = MOUSE_BUTTONS[event.button_index] + return "%s %s" % [DEVICE_MOUSE, mouse_button] + return get_text(event).capitalize() diff --git a/addons/maaacks_game_template/base/nodes/utilities/input_helper.gd.uid b/addons/maaacks_game_template/base/nodes/utilities/input_helper.gd.uid new file mode 100644 index 0000000..7dbb6ea --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/utilities/input_helper.gd.uid @@ -0,0 +1 @@ +uid://6xujceamar4h diff --git a/addons/maaacks_game_template/base/nodes/utilities/pause_menu_controller.gd b/addons/maaacks_game_template/base/nodes/utilities/pause_menu_controller.gd new file mode 100644 index 0000000..38d88f2 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/utilities/pause_menu_controller.gd @@ -0,0 +1,27 @@ +extends Node + +## Node for opening a pause menu when detecting a 'ui_cancel' event. + +@export var pause_menu_packed : PackedScene +@export var focused_viewport : Viewport + +var pause_menu : Node + +func _unhandled_input(event : InputEvent) -> void: + if event.is_action_pressed("ui_cancel"): + if pause_menu.visible: return + if not focused_viewport: + focused_viewport = get_viewport() + var _initial_focus_control = focused_viewport.gui_get_focus_owner() + pause_menu.show() + if pause_menu is CanvasLayer: + await pause_menu.visibility_changed + else: + await pause_menu.hidden + if is_inside_tree() and _initial_focus_control: + _initial_focus_control.grab_focus() + +func _ready() -> void: + pause_menu = pause_menu_packed.instantiate() + pause_menu.hide() + get_tree().current_scene.call_deferred("add_child", pause_menu) diff --git a/addons/maaacks_game_template/base/nodes/utilities/pause_menu_controller.gd.uid b/addons/maaacks_game_template/base/nodes/utilities/pause_menu_controller.gd.uid new file mode 100644 index 0000000..b1138e6 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/utilities/pause_menu_controller.gd.uid @@ -0,0 +1 @@ +uid://cyh0d64pfygbl diff --git a/addons/maaacks_game_template/base/nodes/windows/confirmation_overlaid_window.gd b/addons/maaacks_game_template/base/nodes/windows/confirmation_overlaid_window.gd new file mode 100644 index 0000000..9d1b1fa --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/windows/confirmation_overlaid_window.gd @@ -0,0 +1,20 @@ +@tool +class_name ConfirmationOverlaidWindow +extends OverlaidWindow + +signal confirmed + +@onready var confirm_button : Button = %ConfirmButton + +@export var confirm_button_text : String = "Confirm" : + set(value): + confirm_button_text = value + if update_content and is_inside_tree(): + confirm_button.text = confirm_button_text + +func confirm(): + confirmed.emit() + close() + +func _on_confirm_button_pressed(): + confirm() diff --git a/addons/maaacks_game_template/base/nodes/windows/confirmation_overlaid_window.gd.uid b/addons/maaacks_game_template/base/nodes/windows/confirmation_overlaid_window.gd.uid new file mode 100644 index 0000000..5d647aa --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/windows/confirmation_overlaid_window.gd.uid @@ -0,0 +1 @@ +uid://bgthh72eu0du diff --git a/addons/maaacks_game_template/base/nodes/windows/confirmation_overlaid_window.tscn b/addons/maaacks_game_template/base/nodes/windows/confirmation_overlaid_window.tscn new file mode 100644 index 0000000..b5ce599 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/windows/confirmation_overlaid_window.tscn @@ -0,0 +1,23 @@ +[gd_scene load_steps=3 format=3 uid="uid://cwt4p3bufkke5"] + +[ext_resource type="PackedScene" uid="uid://6gdbfi0172ji" path="res://addons/maaacks_game_template/base/nodes/windows/overlaid_window.tscn" id="1_vfkm2"] +[ext_resource type="Script" uid="uid://bgthh72eu0du" path="res://addons/maaacks_game_template/base/nodes/windows/confirmation_overlaid_window.gd" id="2_sw7p1"] + +[node name="ConfirmationOverlaidWindow" instance=ExtResource("1_vfkm2")] +script = ExtResource("2_sw7p1") +confirm_button_text = "Confirm" +update_content = true +close_button_text = "Cancel" + +[node name="MenuButtons" parent="ContentContainer/BoxContainer/MenuButtonsMargin" index="0"] +vertical = false + +[node name="CloseButton" parent="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="0"] +text = "Cancel" + +[node name="ConfirmButton" type="Button" parent="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="1"] +unique_name_in_owner = true +layout_mode = 2 +text = "Confirm" + +[connection signal="pressed" from="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons/ConfirmButton" to="." method="_on_confirm_button_pressed"] diff --git a/addons/maaacks_game_template/base/nodes/windows/overlaid_window.gd b/addons/maaacks_game_template/base/nodes/windows/overlaid_window.gd new file mode 100644 index 0000000..1894e65 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/windows/overlaid_window.gd @@ -0,0 +1,63 @@ +@tool +class_name OverlaidWindow +extends WindowContainer + +@export var pauses_game : bool = false : + set(value): + pauses_game = value + if pauses_game: + process_mode = PROCESS_MODE_ALWAYS + else: + process_mode = PROCESS_MODE_INHERIT +@export var makes_mouse_visible : bool = true +@export var exclusive : bool = true +@export var exclusive_background_color : Color + +var _initial_pause_state : bool = false +var _initial_focus_mode : FocusMode = FOCUS_ALL +var _initial_mouse_mode : Input.MouseMode +var _initial_focus_control +var _scene_tree : SceneTree +var _exclusive_control_node : ColorRect + +func close() -> void: + if not visible: return + _scene_tree.paused = _initial_pause_state + Input.set_mouse_mode(_initial_mouse_mode) + if is_instance_valid(_initial_focus_control) and _initial_focus_control.is_inside_tree(): + _initial_focus_control.focus_mode = _initial_focus_mode + _initial_focus_control.grab_focus() + if _exclusive_control_node: + _exclusive_control_node.queue_free() + super.close() + +func _overlaid_window_setup(): + if _scene_tree: + _initial_pause_state = _scene_tree.paused + _initial_mouse_mode = Input.get_mouse_mode() + _initial_focus_control = get_viewport().gui_get_focus_owner() + if _initial_focus_control: + _initial_focus_mode = _initial_focus_control.focus_mode + _initial_focus_control.release_focus() + if Engine.is_editor_hint(): return + _scene_tree.paused = pauses_game or _initial_pause_state + if makes_mouse_visible: + Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) + if exclusive: + _exclusive_control_node = ColorRect.new() + _exclusive_control_node.name = self.name + "ExclusiveControl" + _exclusive_control_node.color = exclusive_background_color + _exclusive_control_node.set_anchors_preset(PRESET_FULL_RECT) + add_sibling.call_deferred(_exclusive_control_node) + await _exclusive_control_node.draw + get_parent().move_child(_exclusive_control_node, get_index()) + +func _on_visibility_changed() -> void: + if is_visible_in_tree(): + _overlaid_window_setup() + +func _enter_tree() -> void: + _scene_tree = get_tree() + if not visibility_changed.is_connected(_on_visibility_changed): + visibility_changed.connect(_on_visibility_changed) + _on_visibility_changed() diff --git a/addons/maaacks_game_template/base/nodes/windows/overlaid_window.gd.uid b/addons/maaacks_game_template/base/nodes/windows/overlaid_window.gd.uid new file mode 100644 index 0000000..4a7fdb9 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/windows/overlaid_window.gd.uid @@ -0,0 +1 @@ +uid://xfugmpspqbcc diff --git a/addons/maaacks_game_template/base/nodes/windows/overlaid_window.tscn b/addons/maaacks_game_template/base/nodes/windows/overlaid_window.tscn new file mode 100644 index 0000000..d1ae089 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/windows/overlaid_window.tscn @@ -0,0 +1,12 @@ +[gd_scene load_steps=3 format=3 uid="uid://6gdbfi0172ji"] + +[ext_resource type="Script" uid="uid://xfugmpspqbcc" path="res://addons/maaacks_game_template/base/nodes/windows/overlaid_window.gd" id="1_euyj1"] +[ext_resource type="PackedScene" uid="uid://b2s0kvrx8r2kq" path="res://addons/maaacks_game_template/base/nodes/windows/window_container.tscn" id="2_pmk27"] + +[node name="OverlaidWindow" instance=ExtResource("2_pmk27")] +process_mode = 0 +script = ExtResource("1_euyj1") +pauses_game = false +makes_mouse_visible = true +exclusive = true +exclusive_background_color = Color(0, 0, 0, 0.5) diff --git a/addons/maaacks_game_template/base/nodes/windows/overlaid_window_scene_container.gd b/addons/maaacks_game_template/base/nodes/windows/overlaid_window_scene_container.gd new file mode 100644 index 0000000..c35d5fb --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/windows/overlaid_window_scene_container.gd @@ -0,0 +1,19 @@ +@tool +class_name OverlaidWindowContainer +extends OverlaidWindow + +var instance : Node +@onready var scene_container : Container = %SceneContainer + +@export var packed_scene : PackedScene : + set(value): + packed_scene = value + if is_inside_tree(): + for child in scene_container.get_children(): + child.queue_free() + if packed_scene: + instance = packed_scene.instantiate() + scene_container.add_child(instance) + +func _ready() -> void: + packed_scene = packed_scene diff --git a/addons/maaacks_game_template/base/nodes/windows/overlaid_window_scene_container.gd.uid b/addons/maaacks_game_template/base/nodes/windows/overlaid_window_scene_container.gd.uid new file mode 100644 index 0000000..c049d95 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/windows/overlaid_window_scene_container.gd.uid @@ -0,0 +1 @@ +uid://c6pmyo50c1tqy diff --git a/addons/maaacks_game_template/base/nodes/windows/overlaid_window_scene_container.tscn b/addons/maaacks_game_template/base/nodes/windows/overlaid_window_scene_container.tscn new file mode 100644 index 0000000..453b235 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/windows/overlaid_window_scene_container.tscn @@ -0,0 +1,13 @@ +[gd_scene load_steps=3 format=3 uid="uid://crndfbb22ri4s"] + +[ext_resource type="PackedScene" uid="uid://6gdbfi0172ji" path="res://addons/maaacks_game_template/base/nodes/windows/overlaid_window.tscn" id="1_07348"] +[ext_resource type="Script" uid="uid://c6pmyo50c1tqy" path="res://addons/maaacks_game_template/base/nodes/windows/overlaid_window_scene_container.gd" id="2_p673y"] + +[node name="OverlaidWindowSceneContainer" instance=ExtResource("1_07348")] +script = ExtResource("2_p673y") +packed_scene = null + +[node name="SceneContainer" type="MarginContainer" parent="ContentContainer/BoxContainer/BodyMargin" index="1"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 3 diff --git a/addons/maaacks_game_template/base/nodes/windows/window_container.gd b/addons/maaacks_game_template/base/nodes/windows/window_container.gd new file mode 100644 index 0000000..98ec82f --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/windows/window_container.gd @@ -0,0 +1,80 @@ +@tool +class_name WindowContainer +extends PanelContainer + +signal closed +signal opened + +@export var ui_cancel_closes : bool = true + +@export_group("Content") +@export var update_content : bool = false +@export_multiline var text : String : + set(value): + text = value + if update_content and is_inside_tree(): + description_label.text = text + +@export var close_button_text : String = "Close" : + set(value): + close_button_text = value + if update_content and is_inside_tree(): + close_button.text = close_button_text + +@export_subgroup("Title") +@export var title : String = "Menu" : + set(value): + title = value + if update_content and is_inside_tree(): + title_label.text = title + +@export_range(0, 1000, 1) var title_font_size : int = 16 : + set(value): + title_font_size = value + if update_content and is_inside_tree(): + title_label.set("theme_override_font_sizes/font_size", title_font_size) + +@export var title_visible : bool = true : + set(value): + title_visible = value + if update_content and is_inside_tree(): + title_margin.visible = title_visible + +@onready var content_container : Container = %ContentContainer +@onready var title_label : Label = %TitleLabel +@onready var title_margin : MarginContainer = %TitleMargin +@onready var description_label : RichTextLabel = %DescriptionLabel +@onready var close_button : Button = %CloseButton +@onready var menu_buttons : BoxContainer = %MenuButtons + +func _ready() -> void: + update_content = update_content + text = text + close_button_text = close_button_text + title = title + title_font_size = title_font_size + title_visible = title_visible + +func close() -> void: + if not visible: return + hide() + closed.emit() + +func _handle_cancel_input() -> void: + close() + +func _unhandled_input(event : InputEvent) -> void: + if visible and event.is_action_released("ui_cancel") and ui_cancel_closes: + _handle_cancel_input() + get_viewport().set_input_as_handled() + +func _on_close_button_pressed() -> void: + close() + +func show() -> void: + super.show() + opened.emit() + +func _exit_tree(): + if Engine.is_editor_hint(): return + close() diff --git a/addons/maaacks_game_template/base/nodes/windows/window_container.gd.uid b/addons/maaacks_game_template/base/nodes/windows/window_container.gd.uid new file mode 100644 index 0000000..49e5380 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/windows/window_container.gd.uid @@ -0,0 +1 @@ +uid://b3onujul5qho1 diff --git a/addons/maaacks_game_template/base/nodes/windows/window_container.tscn b/addons/maaacks_game_template/base/nodes/windows/window_container.tscn new file mode 100644 index 0000000..b8f8190 --- /dev/null +++ b/addons/maaacks_game_template/base/nodes/windows/window_container.tscn @@ -0,0 +1,90 @@ +[gd_scene load_steps=3 format=3 uid="uid://b2s0kvrx8r2kq"] + +[ext_resource type="Script" uid="uid://b3onujul5qho1" path="res://addons/maaacks_game_template/base/nodes/windows/window_container.gd" id="1_te2s1"] +[ext_resource type="Script" uid="uid://1nf36h0gms3q" path="res://addons/maaacks_game_template/base/nodes/utilities/capture_focus.gd" id="2_xihbi"] + +[node name="WindowContainer" type="PanelContainer"] +process_mode = 3 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -80.0 +offset_top = -50.0 +offset_right = 80.0 +offset_bottom = 50.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +script = ExtResource("1_te2s1") + +[node name="ContentContainer" type="MarginContainer" parent="."] +unique_name_in_owner = true +layout_mode = 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="BoxContainer" type="BoxContainer" parent="ContentContainer"] +layout_mode = 2 +vertical = true + +[node name="TitleMargin" type="MarginContainer" parent="ContentContainer/BoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +theme_override_constants/margin_left = -14 +theme_override_constants/margin_top = -14 +theme_override_constants/margin_right = -14 +theme_override_constants/margin_bottom = 8 + +[node name="BoxContainer" type="BoxContainer" parent="ContentContainer/BoxContainer/TitleMargin"] +layout_mode = 2 +theme_override_constants/separation = 0 +vertical = true + +[node name="TitleLabel" type="Label" parent="ContentContainer/BoxContainer/TitleMargin/BoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +theme_override_font_sizes/font_size = 16 +text = "Menu" +horizontal_alignment = 1 + +[node name="HSeparator" type="HSeparator" parent="ContentContainer/BoxContainer/TitleMargin/BoxContainer"] +layout_mode = 2 + +[node name="BodyMargin" type="MarginContainer" parent="ContentContainer/BoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 3 + +[node name="DescriptionLabel" type="RichTextLabel" parent="ContentContainer/BoxContainer/BodyMargin"] +unique_name_in_owner = true +layout_mode = 2 +bbcode_enabled = true +fit_content = true +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="MenuButtonsMargin" type="MarginContainer" parent="ContentContainer/BoxContainer"] +layout_mode = 2 +theme_override_constants/margin_top = 8 + +[node name="MenuButtons" type="BoxContainer" parent="ContentContainer/BoxContainer/MenuButtonsMargin"] +unique_name_in_owner = true +custom_minimum_size = Vector2(128, 0) +layout_mode = 2 +size_flags_vertical = 3 +theme_override_constants/separation = 16 +alignment = 1 +vertical = true +script = ExtResource("2_xihbi") + +[node name="CloseButton" type="Button" parent="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons"] +unique_name_in_owner = true +layout_mode = 2 +text = "Close" + +[connection signal="pressed" from="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons/CloseButton" to="." method="_on_close_button_pressed"] diff --git a/addons/maaacks_game_template/base/translations/menus_translations.csv b/addons/maaacks_game_template/base/translations/menus_translations.csv new file mode 100644 index 0000000..287a5b1 --- /dev/null +++ b/addons/maaacks_game_template/base/translations/menus_translations.csv @@ -0,0 +1,104 @@ +keys,en,fr + +___ MAIN MENU,, + +Title,Title,Titre +Subtitle,Subtitle,Sous-titre +New Game,New Game, +Continue,Continue, +Play,Play,Jouer +Level Select,Level Select, +Options,Options,Options +Credits,Credits,Crédits +Exit,Exit,Quitter +"Are you sure you want to start a new game? + +All progress in the current game will be lost.","Are you sure you want to start a new game? + +All progress in the current game will be lost.", + +___ LOADING SCREEN,, +Loading...,Loading...,Chargement... +Still Loading...,Still Loading..., +Loading Complete!,Loading Complete!, +Any Moment Now...,Any Moment Now..., + +___ DIALOGS IN GAME,, + +Win,Win, +Lose,Lose, +Tutorial,Tutorial, +Change Level Color: ,Change Level Color: , +Close,Close, + +Level complete!,Level complete!, +You lost...,You lost...,Vous avez perdu... +You won!,You won!,Vous avez gagné ! +Thanks for playing!,Thanks for playing!,Merci d'avoir joué ! + +Exit Game,Exit Game,Quitter le jeu +Main Menu,Main Menu,Menu principal +Restart,Restart,Recommencer +Continue,Continue,Continuer +Menu,Menu,Menu + +Please Confirm...,Please Confirm...,Veuillez confirmer... +Go back to main menu?,Go back to main menu?,Retourner au menu principal ? +Quit the game?,Quit the game?,Quitter le jeu ? +Cancel,Cancel,Annuler +OK,OK,OK + +__ TUTORIALS,, + +"Click the Win button to progress. +Click the Lose button to try again.","Click the Win button to progress. +Click the Lose button to try again.", +"Progress is saved. +Pressing Continue from the main menu will load the last level played.","Progress is saved. +Pressing Continue from the main menu will load the last level played.", +"The color picker at the bottom-right updates the level state. This change persists until the game is reset. + +The label at the bottom-center displays the current input action detected, if any are setup for the project.","The color picker at the bottom-right updates the level state. This change persists until the game is reset. + +The label at the bottom-center displays the current input action detected, if any are setup for the project.", + +___ OPTIONS MENU,, + +Controls,Controls,Contrôles +Mouse Sensitivity :,Mouse Sensitivity :,Sensibilité souris : +Actions & Inputs,Actions & Inputs,Actions et contrôles +Add,Add,Ajouter +Remove,Remove,Enlever +Assign Key for {action},Assign Key for {action},Choisir le contrôle pour {action} +Listening for input...,Listening for input...,Appuyez sur un bouton... +Press again to confirm...,Press again to confirm...,Appuyez encore pour confirmer... +Focus here to assign inputs.,Focus here to assign inputs.,Mettez le focus ici pour choisir le contrôle. +Already Assigned,Already Assigned,Déjà utilisé +{key} already assigned to {action}.,{key} already assigned to {action}.,{key} est déjà utilisé pour {action}. +Remove Key for {action},Remove Key for {action},Supprimer le contrôle pour {action} +Are you sure you want to remove {key} from {action}?,Are you sure you want to remove {key} from {action}?,Êtes-vous sûr de vouloir supprimer {key} pour {action} ? +Reset,Reset,Réinitialiser + +Audio,Audio,Audio +Master :,Master :,Principal : +Music :,Music :,Musique : +SFX :,SFX :,Effets : +Mute :,Mute :,Silencieux : + +Video,Video,Vidéo +Fullscreen :,Fullscreen :,Plein écran : +Resolution :,Resolution :,Résolution : +Anti-Aliasing :,Anti-Aliasing :,Anticrénelage : +Disabled (Fastest),Disabled (Fastest),Désactivé (Plus rapide) +8x (Slowest),8x (Slowest),8x (Plus lent) +Camera Shake :,Camera Shake :,Secousse Caméra : +Normal,Normal,Normale +Reduced,Reduced,Réduite +Minimal,Minimal,Minimum +None,None,Aucune + +Game,Game,Jeu +Reset Game :,Reset Game :,Réinitialiser le jeu : +Do you want to reset your game data?,Do you want to reset your game data?,Voulez-vous réinitialiser votre partie ? + +Back,Back,Retour diff --git a/addons/maaacks_game_template/base/translations/menus_translations.csv.import b/addons/maaacks_game_template/base/translations/menus_translations.csv.import new file mode 100644 index 0000000..f38bb71 --- /dev/null +++ b/addons/maaacks_game_template/base/translations/menus_translations.csv.import @@ -0,0 +1,19 @@ +[remap] + +importer="csv_translation" +type="Translation" +uid="uid://i6ihop1vp2ei" + +[deps] + +files=["res://addons/maaacks_game_template/base/translations/menus_translations.en.translation", "res://addons/maaacks_game_template/base/translations/menus_translations.fr.translation"] + +source_file="res://addons/maaacks_game_template/base/translations/menus_translations.csv" +dest_files=["res://addons/maaacks_game_template/base/translations/menus_translations.en.translation", "res://addons/maaacks_game_template/base/translations/menus_translations.fr.translation"] + +[params] + +compress=true +delimiter=0 +unescape_keys=false +unescape_translations=true diff --git a/addons/maaacks_game_template/base/translations/menus_translations.en.translation b/addons/maaacks_game_template/base/translations/menus_translations.en.translation new file mode 100644 index 0000000..c17686e Binary files /dev/null and b/addons/maaacks_game_template/base/translations/menus_translations.en.translation differ diff --git a/addons/maaacks_game_template/base/translations/menus_translations.fr.translation b/addons/maaacks_game_template/base/translations/menus_translations.fr.translation new file mode 100644 index 0000000..9721f95 Binary files /dev/null and b/addons/maaacks_game_template/base/translations/menus_translations.fr.translation differ diff --git a/addons/maaacks_game_template/docs/AddingCustomOptions.md b/addons/maaacks_game_template/docs/AddingCustomOptions.md new file mode 100644 index 0000000..5b99eae --- /dev/null +++ b/addons/maaacks_game_template/docs/AddingCustomOptions.md @@ -0,0 +1,46 @@ +# Adding Custom Options + +This page covers adding new buttons, sliders, or editable text fields to the options menus that automatically persist between sessions. + +## To the Menu +Custom options can be added to a menu without any code. + +1. Add an `option_control.tscn` node as a child to a container in a scene. + 1. `slider_option_control.tscn` or `toggle_option_control.tscn` can be used if those types match requirements. In that case, skip step 6. + 2. `list_option_control.tscn` and `vector_2_list_option_control.tscn` are also available, but more complicated. See the `ScreenResolution` example. +3. Select the `OptionControl` node just added, to edit it in the inspector. +4. Add an `Option Name`. This prefills the `Key` string. +5. Select an `Option Section`. This prefills the `Section` string. +6. Add any kind of `Button`, `Slider`, `LineEdit`, or `TextEdit` to the `OptionControl` node. +7. Save the scene. + +## To the Game +For options to have any effect outside of the menus, they will need to be referenced by their `key` and `section` from the `PlayerConfig` class. +``` +PlayerConfig.get_config(key, section) +``` + +For example, here is how to get the player's desired input sensitivity for controlling a player camera. +``` +var mouse_sensitivity : float = PlayerConfig.get_config(AppSettings.INPUT_SECTION, "MouseSensitivity", 1.0) +var joypad_sensitivity : float = PlayerConfig.get_config(AppSettings.INPUT_SECTION, "JoypadSensitivity", 1.0) +``` + +## Validation + Validate the values being stored in your local `player_config.cfg` file. +1. Refer to [Accessing Persistent User Data User](https://docs.godotengine.org/en/stable/tutorials/io/data_paths.html#accessing-persistent-user-data-user) to find Godot user data on your machine. +2. Find the directory that matches your project's name. +3. Open `player_config.cfg` (should be in the top directory of the project). +4. Find the section by the section name in brackets, and the key name followed by an equals. + +For example, here is how the player's desired input sensitivity could appear in the config file. + +``` +[InputSettings] + +MouseSensitivity=1.05 +JoypadSensitivity=0.95 +``` + +> [!NOTE] +> Some settings may not appear until they have been customized. diff --git a/addons/maaacks_game_template/docs/AutomaticUpdating.md b/addons/maaacks_game_template/docs/AutomaticUpdating.md new file mode 100644 index 0000000..66eb9b1 --- /dev/null +++ b/addons/maaacks_game_template/docs/AutomaticUpdating.md @@ -0,0 +1,30 @@ +# Automatic Updating + +This plugin automatically checks GitHub for new releases. When a new release is available, the option to update will appear in the `Project > Tools` menu. + +## Starting an Update + +> [!IMPORTANT] +> Save the state of the project, and close all open scenes and scripts. + +Select the option from `Project > Tools > Update Maaack's Minimal Game Template to v...`. + +A window will pop-up, confirming the choice to update to the latest release. Select `OK`. + +Another window will show progress through downloading, saving, and extracting. + +This effectively deletes the current `addons/maaacks_game_template/` folder and replaces it with a new one. Nothing outside of `addons/` should be affected. + +After, a window should appear confirming a successful update. + +## Disabling Automatic Checking + +You can disable the automatic update checks by going into the Project Settings, and enabling the `maaacks_game_template/disable_update_check` setting. You can then close the window. + +## Issues + +If the option to update does not appear, try restarting the editor, or re-enabling the plugin. + +Updating adds the examples folder into the `addons/maaacks_game_template/` folder, if it had been deleted previously. + +Files already copied from the examples folder will not be affected by an update. However, a mismatch of versions may cause issues, too. If there are no major customizations to the copied files, it is recommended to delete them and recopy from `Project > Tools > Run Maaack's Minimal Game Template Setup...`. \ No newline at end of file diff --git a/addons/maaacks_game_template/docs/BuildAndPublish.md b/addons/maaacks_game_template/docs/BuildAndPublish.md new file mode 100644 index 0000000..143f6b1 --- /dev/null +++ b/addons/maaacks_game_template/docs/BuildAndPublish.md @@ -0,0 +1,304 @@ +## How to Build and Publish my game using Github CICD? + +**GitHub** is a platform that hosts your project’s source code online, making it easy to collaborate, track changes, and share your game with players, testers, or colleagues. + +**CI/CD** (Continuous Integration and Continuous Deployment) refers to automating the process of building, testing, and publishing your game whenever you make updates. The idea is to speed up your game's release process so you can push updates frequently, to fix bugs quicker or add more game content. + +Using GitHub Actions, you can set up **workflows that automatically compile your Godot project** and **upload it to platforms** like itch.io whenever you tag a new release. This saves time, reduces manual errors, and helps keep your build and release process smooth and repeatable. + +**How does it work?** Once everything is set up, publish a new version of your game by creating a new **Github Release**. This will trigger the Github Action, that will build your game in the cloud and publish it to itch.io with a nice version tag. + +> Note: You can set up all of this and still keep your game as a _Draft_ on itch.io. This is great for playtesting! + +## Prerequisites + +Before following this guide, make sure you have the following in place: + +- **GitHub Account & Repository:** Your Godot project should be pushed to a GitHub repository. If you haven’t yet, create one and upload your project files. +- **Itch.io Account:** You’ll need an itch.io account to publish your game. Create one at itch.io if you don’t have it yet. + +## Setup your game build + +### 1. Setup exports for your game in Godot + +First, open your game in Godot. Go to Project > Export... and make sure to add the following exports: + +- `Web` +- `Linux` +- `macOS` +- `Windows Desktop` + +![Godot export](../media/build-and-publish/godot_export.png) + +Then, run the export for one platform manually **at least once.** This will create a `export_presets.cfg` file at the root of your project. + +The `build-and-publish.yml` will **trigger** the build configs in `export_presets.cfg` **by name**. So make sure that your exports names are the same as in the list above. You can change the names or add more build configs with small edits to `build-and-publish.yml`. + +#### Additional Git setup +Some version of Godot will add `export_presets.cfg` to `.gitignore` automatically. You'll want to remove that, so that git checks in your export configuration file with the rest of your code. + +#### Additional MacOS setup + +##### MacOS Bundle name + +For MacOS, you have a field called "Bundle name" with default value `com.game.maaack-template`. Change this to be your game name. + +##### MacOS Notarization + +By default, your built game on MacOS will be flagged as dangerous. Players will need to allow its execution by going into _System Settings > Privacy & Security_, allow the app execution, and restart the game. + +To avoid this, you need to notarize your game, i.e. tell Apple who you are and what is your binary. + +For that, you'll need first to create an Apple developer account (99USD/year). Then, you'll need to adapt the Export configuration of MacOS [using this guide](https://docs.godotengine.org/en/latest/tutorials/export/exporting_for_macos.html#if-you-have-an-apple-developer-id-certificate-and-exporting-from-linux-or-windows) to add **rcodesign** notarization and your Apple tokens. + +### 2. Create `GODOT_VERSION` and `EXPORT_NAME` variables + +Go to your Github repository Settings > Secrets and Variables > Actions. Then, select the **Variables** tab. + +Create two **Repository Variables**: `GODOT_VERSION` and `EXPORT_NAME`. + +> [!NOTE] +> Repository variables will be available for this Github repository only, [but you can do more complex stuff if required](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-variables). +> Using variables is great, because repo admins can still see these values in Github and edit them. + +Change the `EXPORT_NAME` to fit the name of your game. This will be the name of the file your players download. + +By default, the workflow file is made for Godot 4.5, but you can set `GODOT_VERSION` to the version of Godot for your project. This will be used for loading container images and export templates. This workflow file uses [godot-ci](https://github.com/abarichello/godot-ci?tab=readme-ov-file) to build your game, so make sure the Godot version you're referring to is [available on Docker.](https://hub.docker.com/r/barichello/godot-ci/tags) + +### 3. Copy the `build-and-publish.yml` file + +Copy the file `addons/maaacks_game_template/extras/scripts/build-and-publish.yml` into the `.github/workflows` folder at the root of your github repository. + +Then, push the file to github on your main branch. The workflow file will be detected by github as a Github Action. + +### 4. (Optional) Edit the export platforms + +The workflow file is made in two parts: + +1. First, it builds your game +2. Then, it pushes the builds to itch.io + +By default, the workflow file tries to build configs named `Web`, `Linux`, `macOS` and `Windows Desktop`, and will fail if one of the configs is not available. + +#### Deleting a platform + +If you **don't want** to export to one platform, **delete** the build jobs and the publish steps in the `build-and-publish.yml`. + +For example, if you don't want to export for macOS, delete this part which builds the artifact: + +```yml + export-mac: + name: macOS Export + runs-on: ubuntu-24.04 + container: + image: barichello/godot-ci:${{ vars.GODOT_VERSION }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + lfs: true + + - name: Setup + run: | + mkdir -v -p ~/.local/share/godot/export_templates/ + mkdir -v -p ~/.config/ + mv /root/.config/godot ~/.config/godot || true + mv /root/.local/share/godot/export_templates/${GODOT_VERSION}.stable ~/.local/share/godot/export_templates/${GODOT_VERSION}.stable || true + + - name: Mac Build + run: | + mkdir -v -p build/mac + EXPORT_DIR="$(readlink -f build)" + cd $PROJECT_PATH + godot --headless --verbose --export-release "macOS" "$EXPORT_DIR/mac/${EXPORT_NAME}-mac.zip" + + - name: Upload to GitHub Release (if this run is a release) + if: ${{ github.event_name == 'release' }} + uses: svenstaro/upload-release-action@v2 + with: + file: build/mac/${{ env.EXPORT_NAME }}-mac.zip + + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: mac + path: build/mac + +``` + +Remove the job ID (ie. `export-mac`) from the needs of the `publish-builds` job: +```yml + publish-builds: + name: Publish Builds + needs: [export-web, export-windows, export-linux, export-mac] +``` + +And remove this part, which publishes it to itch.io: + +```yml + - name: Upload to Itch.io - macOS + run: | + ./butler push builds/mac ${{ env.ITCH_USERNAME }}/${{ env.ITCH_GAME }}:mac --userversion "${{ steps.version.outputs.version }}" +``` + +#### Adding a platform +If you want to export to a **new** platform, **copy paste** the build job and add a new step to itch.io publication. + +In the build job, change: + 1. the name of the **export config** that you created in Godot. + 2. the paths in which the build artifact is created. + +For example: + +```yml +- name: NEW_PLATFORM Build + run: | + mkdir -v -p build/NEW_PLATFORM + EXPORT_DIR="$(readlink -f build)" + cd $PROJECT_PATH + godot --headless --verbose --export-release "BUILD_CONFIG_NAME" "$EXPORT_DIR/NEW_PLATFORM/$EXPORT_NAME.zip" +``` + +In the itch.io publication step, make sure to change the path and the tag. + +```yml +- name: Upload to Itch.io - NEW_PLATFORM_TAG + run: | + ./butler push builds/NEW_PLATFORM ${{ env.ITCH_USERNAME }}/${{ env.ITCH_GAME }}:NEW_PLATFORM_TAG --userversion "${{ steps.version.outputs.version }}" + +``` + +## Setup Itch.io publication + +### 1. Create a new project on itch.io + +1. Go to [itch.io](https://itch.io/), click on the top right and **Upload a New Project**. + +2. Fill in the game name and any information you want, but don't upload any file. + +3. If you plan to have a **Web build**, select the **Kind of project** to be **HTML** instead of **Downloadable**. + +4. Save the project as a **Draft** (you can change this to public later, once you tested that everything works). + +### 2. Create `ITCH_USERNAME` and `ITCH_GAME` variables + +To find your itch.io username and the name of your game, look at the url of your project: `https://your-username.itch.io/your-game`. The username is the first part of the URL, and the game name is in the last part. + +Then, go to your Github repository Settings > Secrets and Variables > Actions. Then, select the **Variables** tab. + +Create two **Repository Variables**: `ITCH_USERNAME` and `ITCH_GAME`. + +You should have something like this (with your real username and your real game name instead, and any other repository variables): + +![github variables](../media/build-and-publish/github-variables.png) + +### 3. Create a `BUTLER_API_KEY` Github secret + +1. Install [butler.](https://itch.io/docs/butler/installing.html) This is the official CLI tool for itch.io + +2. Unzip and make sure the bin is executable + + ```bash + chmod +x butler + ``` + +3. Run + + ```bash + butler login + ``` + + This should open your browser. Login and allow butler to access your account. + + ![Authorize butler](../media/build-and-publish/authorize_butler.png) + + In the terminal, the login flow will conclude with something like this: + + ``` + Authenticated successfully! Saving key in /Users/username/Library/Application Support/itch/butler_creds... + ``` + + **Note:** if you're already logged in into butler but forgot the path to credentials, use `butler login -h` to show the default location of `butler_creds`. + +4. Get your butler API key by reading the content of this file. Beware of spaces in the filepath! This will show you a 40 characters string which is your butler API key. + + ```bash + cat "/Users/username/Library/Application Support/itch/butler_creds" + ``` + + > **Warning:** your butler API key is sensitive and secret. **Do not** share it with anyone, **do not** commit it to your repository, and **do not** add it directly to the workflow file. + +5. [Create a new Github secret](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets) for your Github repository. Go into Settings > Secrets and Variables > Actions and select the **Secrets** tab. + + Create a new **Repository Secret** with name `BUTLER_API_KEY` (this secret will be available only in this repository). Inside, paste the 40 characters string of the previous step. + + ![github secrets](../media/build-and-publish/github-secrets.png) + + > It's important to use Github Secrets here because their values are encrypted and hidden from everyone, even repo admins. This ensures privacy and security. + +## How to publish the game? + +Congrats, you're ready to create a new Github Release and automatically publish updates! + +When you’re ready to publish a new version of your game, create a **GitHub release** tied to a version tag on the `main` branch. Using releases helps track updates, distribute builds, and communicate changes to players or testers. + +A new release will trigger the `build-and-publish.yml` workflow, which will **build your game** in the cloud and **publish it** to itch.io (if everything is setup). + +1. Ensure all desired changes are merged into the `main` branch. This is the version that'll get built and published. +2. On Github, go to **Release**, then **draft a new release** ([here is a step by step guide](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository#creating-a-release)). Create a new tag on you `main` branch using [semantic versioning](https://semver.org/). As a best practice, also prefix it with `v` in Github. + - `x.0.0` — Major Release. Large updates or milestones (e.g., new game systems, overhauled visuals, major gameplay changes). Example: `v1.0.0` for the full launch. + - `x.y.0` — Minor Update. New content or features that expand gameplay but remain backward-compatible. Example: `v1.1.0` for new levels or mechanics. + - `x.y.z` — Patch / Hotfix. Small updates, bug fixes, performance improvements, or balancing tweaks. Example: `v1.1.3` for fixing a crash or visual glitch. +3. Publish the release. This will trigger the CICD in Github Actions. Monitor its execution and check for errors in the **Actions** tab on Github. + +## Troubleshooting: Enable the HTML / Playable version of your game on itch + +It may happen that you don't see the HTML version of your game as playable, but just as a file. + +![no html](../media/build-and-publish/itch_html_missing.png) + +What you need to do is edit your itch project to change the **Kind of project** to be **HTML** instead of **Downloadable**. + +![kind of project](../media/build-and-publish/itch_kind_of_project.png) + +Then, edit the `html5` channel and toggle **This file will be played in the browser**. + +![html5 setting](../media/build-and-publish/itch_html_setting.png) + +Going back to your project page, you should now see the HTML version of your game playable in the browser on itch.io page. + +![Playable game](../media/build-and-publish/itch_playable.png) + +## Next steps + +Once your CI/CD pipeline is running smoothly, take it a step further: + +- **Pre-Release Testing:** Add a test stage in your workflow to validate your project before publishing (for example, by running Godot unit tests or verifying builds). +- **Multi-Platform Deployment:** Add Android and iOS build, or remove the builds you don't use. +- **Other Distribution Platforms:** Adapt the CI/CD pipeline to push releases to other platforms like Steam, Google Play, App Store, Epic Games Store... +- **Add notarization for MacOS:** That's a best practice for a smoother experience. + +## Sources + +- **GitHub Documentation** + + - [Creating a Release on GitHub](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository#creating-a-release) + - [Using GitHub Secrets in Workflows](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets) + +- **Semantic Versioning** + + - [Semantic Versioning 2.0.0](https://semver.org/) + +- **Godot CI / Docker** + + - [abarichello/godot-ci (GitHub)](https://github.com/abarichello/godot-ci?tab=readme-ov-file) + - [barichello/godot-ci Docker Hub Tags](https://hub.docker.com/r/barichello/godot-ci/tags) + +- **Godot Engine Documentation** + + - [Exporting for macOS (with Apple Developer ID)](https://docs.godotengine.org/en/latest/tutorials/export/exporting_for_macos.html#if-you-have-an-apple-developer-id-certificate-and-exporting-from-linux-or-windows) + +- **itch.io Resources** + + - [itch.io Main Website](https://itch.io/) + - [Butler CLI Installation Guide](https://itch.io/docs/butler/installing.html) diff --git a/addons/maaacks_game_template/docs/ExistingProject.md b/addons/maaacks_game_template/docs/ExistingProject.md new file mode 100644 index 0000000..5e36bae --- /dev/null +++ b/addons/maaacks_game_template/docs/ExistingProject.md @@ -0,0 +1,48 @@ +# Existing Project + +These instructions assume starting with just the contents of `addons/` and going through the installer to copy the examples content into your project. This will be the case when installing the *plugin* version in the Godot Asset Library. + +To revisit any part of the initial setup, find the `Setup Wizard` at `Project > Tools > Run Maaack's Minimal Game Template Setup...`. Example files can be re-copied from the `Setup Wizard`, assuming they have not been deleted. + +1. Update the project’s name in the main menu. + + + 1. Open `main_menu_with_animations.tscn`. + 2. Select the `TitleLabel` node. + 3. The `Text` should match the project's name (in the project's settings). + 1. If `Text` is customized, set `Auto Update` to false. + 4. Select the `SubtitleLabelNode` node and customize the `Text` as desired. + 5. Save the scene. + + +2. Link the main menu to a custom game scene (skip if using the example game scene). + + + 1. Open `main_menu_with_animations.tscn`. + 2. Select the `MainMenu` node. + 3. Update `Game Scene Path` to the path of the project's game scene. + 4. Save the scene. + + +3. Add / remove configurable settings to / from menus. + + + 1. Open `[master|mini|audio|visual|input]_options_menu.tscn` scenes to edit their options. + 2. If an option is not desired, it can always be hidden, or removed entirely (sometimes with some additional work). + 3. If a new option is desired, refer to [Adding Custom Options.](/addons/maaacks_game_template/docs/AddingCustomOptions.md) + + +4. Update the game credits / attribution. + + + 1. Open `credits_label.tscn`. + 2. Update `CreditsLabel` with your desired text. BBCode allows for some formatting. + 3. Save the scene. + + +5. Continue with: + + 1. [Setting up the Main Menu.](/addons/maaacks_game_template/docs/MainMenuSetup.md) + 2. [Setting up a Game Scene.](/addons/maaacks_game_template/docs/GameSceneSetup.md) + 3. [Adding icons to the Input Options.](/addons/maaacks_game_template/docs/InputIconMapping.md) + 4. [Adding Custom Options.](/addons/maaacks_game_template/docs/AddingCustomOptions.md) diff --git a/addons/maaacks_game_template/docs/GameSceneSetup.md b/addons/maaacks_game_template/docs/GameSceneSetup.md new file mode 100644 index 0000000..1085a99 --- /dev/null +++ b/addons/maaacks_game_template/docs/GameSceneSetup.md @@ -0,0 +1,96 @@ +# Game Scene Setup + +When setting up a game scene, it is useful to refer to the `game_scene/game_ui.tscn` included in the examples. + +There are a few parts to setting up a basic game scene, as done in the `GameUI` example used in the template. + +## Pausing +The `PauseMenuController` node can be added to the tree, or the `pause_menu_controller.gd` script may be attached to an empty `Node`. Selecting the node should then allow for setting the `pause_menu_packed` value in the inspector. Set it to the `pause_menu.tscn` scene and save. + +This should be enough to capture when the `ui-cancel` input action is pressed in-game. On keyboards, this is commonly the `Esc` key. + +## Level Loading +Some level loading scripts are provided with the examples. They load levels in order from a list, or dynamically by file paths. + +The `LevelManager` manages the progress through levels. It works with a level loader and can open menus when players win or lose. With a child `SceneLister`, it manages progression linearly. Otherwise, it will rely on the levels themselves providing the path to the next level. It can either be assigned a starting level path (for open world progression) or start from the first level in a scene lister (for linear level progression). + +A `LevelLoader` loads levels, attaches them to a container, and manages a loading screen. It must be provided with a `level_container` in the scene. The example uses the `SubViewport`, but any leaf node (ie. node without children) in the scene should work. An optional `level_loading_screen` in the scene can be attached to show progress of loading levels. + +### Linear Level Progress +With linear progression, the path in the `starting_level_path` setting can be removed. Levels can be added to the `SceneLister` by either selecting a directory to automatically find scenes, or populating the files array manually. + +If using the level select scene, then the `SceneLister` there will also need to be updated to match. + +By default, the manager will open the first level from the `SceneLister`. It'll then set the checkpoint to the next level in the list when the current level is won. When winning the last level, it'll load the window for the game being won. + +### Non-Linear Level Progress +Alternatively, with open world progression, the reference in the `scene_lister` setting can be removed. Instead, the path to the next level is expected to be provided by the current level. The example levels demonstrate this with the `next_level_path` setting. + +By default, the manager will open `starting_level_path`. It'll then set the checkpoint to the next level sent in the `level_won` or `level_changed` signal from the current level. If no level path is provided, it'll load the window for the game being won. + +### Games without levels +Level Loading is not required if the entire game takes place in one scene. + +In that case, the following nodes can be safely removed: +* LevelLoader +* LevelManager +* LevelLoadingScreen + +The single level scene can then be added directly to the `SubViewport`, another container, or the root node. + +To manage the win and lose screens and transitioning to other scenes, add a `Node` and attach the `win_lose_manager.gd` script. Inspect the node to attach the win / lose screens and paths. The `game_won()` or `game_lost()` will then need to be called when gameplay conditions are met. + +## SubViewports +The game example has the levels loaded into a `SubViewport` node, contained within a `SubViewportContainer`. This has a couple of advantages. + +- Separates elements intended to appear inside the game world from those intended to appear on a layer above it. +- Allows setting a fixed resolution for the game, like pixel art games. +- Allows setting rendering settings, like anti-aliasing, on the `SubViewport`. +- Supports easily adding visual effects with shaders on the `SubViewportContainer`. +- Visual effects can be added to the game world without hurting the readability of the UI. + +It has some disadvantages, as well. + +- Locks the viewport resolution if any scaling is enabled, which is not ideal for 3D games. +- Requires enabling Audio Listeners to hear audio from the game world. +- Extra processing overhead for the viewport layer. + +If a subviewport does not work well for the game, use any empty `Node` as the game world or level container, instead. + +### Pixel Art Games +If working with a pixel art game, often the goal is that the number of art pixels on-screen is to remain the same regardless of screen resolution. As in, the art scales with the monitor, rather than bigger monitors showing more of a scene. This is done by setting the viewport size in the project settings, and setting the stretch mode to either `canvas_mode` or `viewport`. + +If a higher resolution is desired for the menus and UI than the game, then the project viewport size should be set to a multiple of the desired game window size. Then set the stretch shrink in `SubViewportContainer` to the multiple of the resolution. For example, if the game is at `640x360`, then the project viewport size can be set to `1280x720`, and the stretch shrink set to `2` (`1280x720 / 2 = 640x360`). Finally, set the texture filter on the `SubViewportContainer` to `Nearest`. + +### Mouse Interaction +If trying to detect `mouse_enter` and `mouse_exit` events on areas inside the game world, enable physics object picking on the `SubViewport`. + +## Read Inputs +Generally, any game is going to require reading some inputs from the player. Where in the scene hierarchy the reading occurs is best answered with simplicity. + +If the game involves moving a player character, then the inputs for movements could be read by a `player_character.gd` script overriding the `_process(delta)` or `_input(event)` methods. + +If the game involves sending commands to multiple units, then those inputs probably should be read by a `game_ui.gd` script, that then propagates those calls further down the chain. + +## Win & Lose Screens +The example includes win and lose screens. These are triggered by the `LevelManager` when a level is won or lost. + +``` +func _load_level_complete_screen_or_next_level(): + if level_won_scene: + var instance = level_won_scene.instantiate() + get_tree().current_scene.add_child(instance) + ... + else: + _load_next_level() +``` +Winning on the last level results in loading a win screen or ending for the game. + +``` +func _on_level_won(): + if is_on_last_level(): + _load_win_screen_or_ending() + else: + _load_level_won_screen_or_next_level() +``` +The `LevelManager` will need to be linked to direct back to the main menu and optionally forward to an end credits. \ No newline at end of file diff --git a/addons/maaacks_game_template/docs/GamesMade.md b/addons/maaacks_game_template/docs/GamesMade.md new file mode 100644 index 0000000..a3eb879 --- /dev/null +++ b/addons/maaacks_game_template/docs/GamesMade.md @@ -0,0 +1,66 @@ +# Games +This page features games using Maaack's Godot Game Template and/or plugins. + +If you have a game you'd like to share, join the [Discord server](https://discord.gg/AyZrJh5AMp ) and post a link to your game in #showcase. + +## Featured + +| Baking Godium | Spud Customs | Rent Seek Kill | +| :-------:| :-------: | :-------: | +| ![Baking Godium](/addons/maaacks_game_template/media/thumbnail-game-baking-godium.png) | ![Spud Customs](/addons/maaacks_game_template/media/thumbnail-game-spud-customs.png) | ![Rent-Seek-Kill](/addons/maaacks_game_template/media/thumbnail-game-rent-seek-kill.png) | +| [Play on itch.io](https://maaack.itch.io/baking-godium) | [Find on Steam](https://store.steampowered.com/app/3291880/Spud_Customs/) | [Play on itch.io](https://xandruher.itch.io/rent-seek-kill) | + + +## All Shared +### 2025 +https://sevadusk.itch.io/liferoot +https://maaack.itch.io/baking-godium +https://baconeggsrl.itch.io/umbra-city +https://store.steampowered.com/app/3911550/Warp_Marked_Demo/ +https://maaack.itch.io/kobo-expansion +https://keur-collectif.itch.io/heartfix-express-demo +https://spacecheese.itch.io/cia +https://acul4321.itch.io/bleep +https://redspine.itch.io/gmtk-game-jam-2025 +https://zunarii.itch.io/loopinball +https://dragonruler1000.itch.io/beep +https://store.steampowered.com/app/3751730/Loan_Shark/ +https://parallaxrat.itch.io/no-mans-land +https://baconeggsrl.itch.io/sprouts-journey +https://maaack.itch.io/indys-expedition-2 +https://maaack.itch.io/absurd-herd +https://maaack.itch.io/dungeon-fantasy-fashion-show +https://plexsoup.itch.io/factoriohno +https://maaack.itch.io/furnace-in-the-archive +https://schinken.itch.io/low-ink + +### 2024 +https://store.steampowered.com/app/3291880/Spud_Customs/ (Source: https://github.com/Lost-Rabbit-Digital/SpudCustoms) +https://glockenberg.itch.io/icefire-temple +https://maaack.itch.io/backroom-labyrinths +https://maaack.itch.io/haunted-circuits +https://maaack.itch.io/talk-up-the-tower +https://marinaaaa.itch.io/meowntaineer +https://maaack.itch.io/a-darkness-like-gravity +https://maaack.itch.io/lore-of-the-wild-gwj-70 +https://maaack.itch.io/infinite-horizon +https://elidef.itch.io/forge-ur-boss +https://maaack.itch.io/forgeomino +https://xandruher.itch.io/rent-seek-kill +https://maaack.itch.io/blind-escape-gwj-66-edition +https://justaguyjustaguy.itch.io/nannybot-overload +https://maaack.itch.io/the-last-host-boss-rush +https://kyveri.itch.io/riverking + +### 2023 +https://xandruher.itch.io/spectral-war +https://maaack.itch.io/the-cat-with-eight-gwj-63-edition +https://maaack.itch.io/harvest-hill-gwj-62-edition +https://shoddygames.itch.io/once-summoned +https://maaack.itch.io/the-last-host +https://maaack.itch.io/do-androids-dream-gwj-55-edition +https://maaack.itch.io/character-builder-gwj-53-edition + +### 2022 +https://maaack.itch.io/rit-dot-wav +https://maaack.itch.io/supercritical-a-post-apocalyptic-bonsai diff --git a/addons/maaacks_game_template/docs/HowPartsWork.md b/addons/maaacks_game_template/docs/HowPartsWork.md new file mode 100644 index 0000000..184575c --- /dev/null +++ b/addons/maaacks_game_template/docs/HowPartsWork.md @@ -0,0 +1,12 @@ +# How Parts Work + +This page features snippets of extra documentation on key pieces of the plugin. It was previously included in the README. + +- `scene_loader.tscn` is an autoloaded scene. It can load scenes in the background or with a loading screen (`loading_screen.tscn` by default). +- `opening.tscn` is a simple scene for fading in/out a few images at the start of the game. It then loads the next scene (`main_menu.tscn`). +- `main_menu.tscn` is where a player can start the game, change settings, watch credits, or quit. It can link to the path of a game scene to play, and the packed scene of an options menu to use. +- `option_control.tscn` and its inherited scenes are used for most configurable options in the menus. They work with `player_config.gd` to keep settings persistent between runs. +- The `PauseMenuController` can be set to load `pause_menu.tscn` when triggering `ui-cancel`. +- `pause_menu.tscn` is a type of `OverlaidMenu` with the `pauses_game` flag set to true. It will store the previously focused UI element, and return focus to it when closed. +- `capture_focus.gd` is attached to container nodes throughout the UI. It focuses onto UI elements when they are shown, allowing for easier navigation without a mouse. +- `game_ui.tscn` is a demo game scene that displays recognized action inputs, and features the `PauseMenuController` node, the `LevelLoader` node to load levels into a container, and `LevelManager` to manage level progress and show menus in case of a win or loss. \ No newline at end of file diff --git a/addons/maaacks_game_template/docs/InputIconMapping.md b/addons/maaacks_game_template/docs/InputIconMapping.md new file mode 100644 index 0000000..9282246 --- /dev/null +++ b/addons/maaacks_game_template/docs/InputIconMapping.md @@ -0,0 +1,165 @@ +# Input Icon Mapping + +The `InputIconMapper` in `input_options_menu.tscn` is a generalized tool meant to be broadly compatible with freely licensed icon asset packs. Instructions on how to use it with a few of these packs are provided, with links to download them from their creator's page. + +## Kenney Input Prompts + +### Automatic + +> [!IMPORTANT] +> Save the state of the project, and close all open scenes and scripts. + +With the project open, select `Project > Tools > Run Maaack's Minimal Game Template Setup...`. + +In the `Setup Wizard` window next to "Add Input Prompt Icons", click `Run`. + +In the next window, select a style and then wait for the icons to download, extract, and setup. + +If the icons have already been installed before, you will be presented with the option to skip re-downloading. + +> [!WARNING] +> This may crash the editor. +> In that event, check if the process completed, and try running the setup again. + +### Manual + +Available from [kenney.nl](https://kenney.nl/assets/input-prompts) and [itch.io](https://kenney-assets.itch.io/input-prompts). + +This pack is organized by `Device/IconType`. The `IconTypes` for each device are just `Default`, `Vector`, or `Double`. These instructions will assume using `Default`. In the inspector of `InputIconMapper`, set the `directories` to include the subdirectories of the asset pack. +* `.../kenney_input-prompts/Keyboard & Mouse/Default` +* `.../kenney_input-prompts/Generic/Default` +* `.../kenney_input-prompts/Xbox Series/Default` +* `.../kenney_input-prompts/PlayStation Series/Default` +* `.../kenney_input-prompts/Nintendo Switch/Default` +* `.../kenney_input-prompts/Steam Deck/Default` + +Set `filtered_strings` to: +* `keyboard` +* `color` +* `button` +* `arrow` + +Set `replace_strings` with the key pairs: +* `"Capslock": "Caps Lock"` +* `"Generic Stick": "Generic Left Stick"` +* `"Guide": "Home"` +* `"Slash Back": "Back Slash"` +* `"Slash Forward": "Slash"` +* `"Stick L": "Left Stick"` +* `"Stick R": "Right Stick"` +* `"Trigger L 1": "Left Shoulder"` +* `"Trigger L 2": "Left Trigger"` +* `"Trigger R 1": "Right Shoulder"` +* `"Trigger R 2": "Right Trigger"` + +#### Filled Icons +![Kenney Filled Icons](../media/screenshot-5-kenney-2.png) +Under the `FileLister` properties of the `InputIconMapper`, expand the `Constraints` and `Advanced Search` tabs. Set `ends_with=".png"` and `not_ends_with="outline.png"`. + +Press `Refresh Files`. + +If you want to use colored icons, in `prioritized_strings` add `color`. Otherwise set `filter="color"`. + +Press `Match Icons to Inputs`. + +Validate the results by inspecting the `matching_icons` dictionary. + +#### Outlined Icons +![Kenney Outlined Icons](../media/screenshot-5-kenney-4.png) +Not all icons have outlined versions, so we will end up including the filled icons as fallback, and prioritizing outlined. + +Under the `FileLister` properties of the `InputIconMapper`, expand the `Constraints` and `Advanced Search` export groups. Set `ends_with=".png"`. + +Press `Refresh Files`. + +Add to `filtered_strings`: +* `outline` + +In `prioritized_strings` add `outline`. If you want to use colored icons, in `prioritized_strings` add `color`, too. Otherwise set `filter="color"`. + +Press `Match Icons to Inputs`. + +Validate the results by inspecting the `matching_icons` dictionary. + +## Kenny Input Prompts Pixel 16x + +Incompatible: File names not useable. + +## Xelu 's Free Controller & Key Prompts + +![Xelu's Icons](../media/screenshot-5-xelu-2.png) +Available from [thoseawesomeguys.com](https://thoseawesomeguys.com/prompts/). + +This pack is organized by `Device`. In the inspector of `InputIconMapper`, set the `directories` to include the subdirectories of the asset pack. Assumes using the `Dark` icon set with the keyboard and mouse. +* `.../Xelu_Free_Controller&Key_Prompts/Keyboard & Mouse/Dark` +* `.../Xelu_Free_Controller&Key_Prompts/Xbox Series` +* `.../Xelu_Free_Controller&Key_Prompts/PS5` +* `.../Xelu_Free_Controller&Key_Prompts/Switch` +* `.../Xelu_Free_Controller&Key_Prompts/Steam Deck` + +Under the `FileLister` properties of the `InputIconMapper`, expand the `Constraints` and `Advanced Search` tabs. Set `ends_with=".png"`. + +Press `Refresh Files`. + +Set `filtered_strings` to: +* `dark` +* `key` + +Set `replace_strings` with the key pairs: +* `"Ps 5": "Playstation"` +* `"Xbox Series X": "Xbox"` +* `"Steam Deck": "Steamdeck"` +* `"L 1": "Left Shoulder"` +* `"R 1": "Right Shoulder"` +* `"L 2": "Left Trigger"` +* `"R 2": "Right Trigger"` +* `"Click": "Press"` + +Set `add_stick_directions=true`. + +Press `Match Icons to Inputs`. + +Validate the results by inspecting the `matching_icons` dictionary. + +Since `Generic` device icons are not available, set `initial_joypad_device` to either `Xbox`, `Playstation`, `Switch`, or `Steamdeck`. + +## Free Icon Pack for Unity & Unreal – 1500+ Input Icons for Game UI + +![Julio Cacko's Icons](../media/screenshot-5-juliocacko-2.png) +Available from [itch.io](https://juliocacko.itch.io/free-input-prompts). + +This pack is organized by `Device/IconType`. In the inspector of `InputIconMapper`, set the `directories` to include the subdirectories of the asset pack. Assumes using the `Dark` icon set with the keyboard and mouse, and `Default` for the others. +* `.../Source/Keyboard_Mouse/Dark` +* `.../Source/P4Gamepad/Default` +* `.../Source/XGamepad/Default` +* `.../Source/SGamepad/Default` + +Under the `FileLister` properties of the `InputIconMapper`, expand the `Constraints` and `Advanced Search` tabs. Set `ends_with=".png"`. + +Press `Refresh Files`. + +In `prioritized_strings`, add either `color` or `white`, depending on what icons you prefer. + +Set `filtered_strings` to: +* `dark` +* `key` +* `t` +* `color` +* `white` + +Set `replace_strings` with the key pairs: +* `"P 4": "Playstation"` +* `"X": "Xbox"` +* `"S": "Switch"` +* `"L": "Left Stick"` +* `"R": "Right Stick"` +* `"Left Stick 1": "Left Shoulder"` +* `"Right Stick 1": "Right Shoulder"` +* `"Left Stick 2": "Left Trigger"` +* `"Right Stick 2": "Right Trigger"` + +Press `Match Icons to Inputs`. + +Validate the results by inspecting the `matching_icons` dictionary. + +Since `Generic` device icons are not available, set `initial_joypad_device` to either `Xbox`, `Playstation`, or `Switch`. diff --git a/addons/maaacks_game_template/docs/JoypadInputs.md b/addons/maaacks_game_template/docs/JoypadInputs.md new file mode 100644 index 0000000..0515080 --- /dev/null +++ b/addons/maaacks_game_template/docs/JoypadInputs.md @@ -0,0 +1,31 @@ +# Joypad Inputs + +This page covers topics related to working with joypads. + +## Recognized Devices + +- Xbox +- Playstation 4 +- Playstation 5 + +### Unconfirmed + +- Switch +- Steam Deck + +## Added UI Inputs + +There is a `override.cfg` in the project root directory that adds a few additional inputs to the project's built-in UI actions. + +These additional inputs are for joypads and include the following: + +- `UI Accept`: A Button (Xbox A / Sony X) +- `UI Cancel`: Back Button (Xbox Back / Sony Select) +- `UI Page Up`: Left Shoulder (Xbox LB / Sony L1) +- `UI Page Down`: Right Shoulder (Xbox RB / Sony R2) + +However, for these to work in exported versions of the project, the inputs need to either be added manually to the project's built-in actions, or `override.cfg` will need to be included in the exports. The latter can be done by including the pattern (`*.cfg`) in **Filters to export non-resource files/folders** under the *Resources* tab of the *Export* window. + +## Web Builds + +Godot (or the template) currently does not support joypad device detection on the web. If icons are being used for input remapping, the joypad icons will *not* update automatically to match a new detected controller. \ No newline at end of file diff --git a/addons/maaacks_game_template/docs/MainMenuSetup.md b/addons/maaacks_game_template/docs/MainMenuSetup.md new file mode 100644 index 0000000..fc4f265 --- /dev/null +++ b/addons/maaacks_game_template/docs/MainMenuSetup.md @@ -0,0 +1,34 @@ +# Main Menu Setup + +These are instructions for further editing the menus. Basic instructions are available in the [README](/addons/maaacks_game_template/README.md#usage). + +## Inheritance + +Most example scenes in the template inherit from scenes in `addons`. This is useful for developing of the plugin, but often less useful for those using it. When editing the example scenes, any nodes inherited from a parent scene are highlighted in yellow in the scene tree. Inherited nodes cannot be edited like native nodes. Therefore, it is recommended to first right-click on the root node, and select `Clear Inheritance`. You'll get a warning that this cannot be undone, but it's okay. You probably won't need to undo it, and if you do, there are solutions. + +## Visual Placement + +The positions and anchor presets of the UI elements can be adjusted to match most designs with ease. Buttons can be centered, right or left justfied, or arranged horizontally. Most visual UI elements are contained within `MarginContainer` and `Control` nodes that allow for fine-tuning of placement. + +## Scene Structure +Some designs may require rearranging the nodes in the scene tree. This is easier once the inheritance to the parent scene is cleared. However, if editing `main_menu_with_animations.tscn`, keep in mind that there are animations, and moving elements outside of the animated containers may have undesired effects. + +## 3D Background +When adding a 3D background to the menu, it is recommended to use a `SubViewportContainer` in place of or right above the `BackgroundTextureRect`. Then add a `SubViewport` to it, and finally the 3D world node to that. This structure gives fine-tune control of scaling, allows for layering 3D views when they have transparency, and makes it easy to add a texture shader to the whole background. + +## Level Select + +A basic level select scene is available to add to the menu. A button can be added to the menu button container that calls `_open_sub_menu()` and passes in the packed scene of the level select menu. Refer to the options and credits button code in `main_menu.gd` for examples. + +Levels can be added to the menu by inspecting the `SceneLister` and either selecting a directory to automatically read scene files from, or populating the files array manually. + +## Theming +It is recommended to have a custom theme for a project. Create a theme resource file or use one of the ones provided with the template and set it as the custom theme in the project settings. Any changes made to the theme file will then apply automatically to the whole project. + +The main UI elements that are used throughout the project that require theming for customization are: +- Button +- Label +- PanelContainer +- ProgressBar +- TabContainer +- Tree \ No newline at end of file diff --git a/addons/maaacks_game_template/docs/MovingFiles.md b/addons/maaacks_game_template/docs/MovingFiles.md new file mode 100644 index 0000000..1632ac0 --- /dev/null +++ b/addons/maaacks_game_template/docs/MovingFiles.md @@ -0,0 +1,33 @@ +# Moving Files + +This page covers some tips for rearranging files to an individual developer's preference. + +> [!WARNING] +> Backup your project before attempting to rearrange files. +> You assume any risk. + +## Move Files in the Editor + +Use the editor to move files around, as this makes sure that `.uid` files get moved with `.gd` files, external resource references will get updated in `.tscn` files, and paths in project settings get updated. + +UIDs do help with moving files outside of the editor, but not all scenes will have UIDs set if they've just recently been copied from the examples. + +## Update File Paths + +The flow of scenes in the template by default goes `Opening -> Main Menu -> Game Scene -> Ending Scene`. + +The `Opening` is referenced in the project settings, and will get automatically update if moved in the editor. + +The rest have their default paths stored in the `AppConfig` autoload. These do not get automatically updated, so the developer must update these paths if they change. + +Alternatively, the developer can specify paths in the scenes that reference the other scenes by path. These include: +* `opening.tscn` +* `main_menu.tscn` +* `main_menu_with_animations.tscn` +* `pause_menu.tscn` +* `game_ui.tscn` (`level_manager.gd`) +* `end_credits.tscn` + +## Internal Details + +File paths, stored as strings, do not get automatically updated by the editor when their target moves. Paths are used when asynchronous loading of scenes (ie. using `SceneLoader`) is preferred, primarily for memory management. diff --git a/addons/maaacks_game_template/docs/NewProject.md b/addons/maaacks_game_template/docs/NewProject.md new file mode 100644 index 0000000..0bb8baa --- /dev/null +++ b/addons/maaacks_game_template/docs/NewProject.md @@ -0,0 +1,57 @@ +# New Projects + +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 accessible from the Project Manager window. + + +1. Finish setup. + + 1. Delete duplicate example files. + 1. Go to `Project > Tools > Run Maaack's Minimal Game Template Setup...`. + 2. In the `Setup Wizard` window next to "Delete Example Files", click `Run`. + 3. In the next window, select `Yes` to continue with removing the example files. + + 2. Update autoload file paths. + 1. Go to `Project > Tools > Run Maaack's Minimal Game Template Setup...`. + 2. In the `Setup Wizard` window next to "Update Autoload Paths", click `Run`. + + 3. Set a default theme. + 1. Go to `Project > Tools > Run Maaack's Minimal Game Template Setup...`. + 2. In the `Setup Wizard` window next to "Set the Default Theme", click `Run`. + 3. In the next window, select the desired theme from the preview and select `Yes` to set it as the project's default theme. + +2. Update the project’s name. + + + 1. Go to `Project > Project Settings… > General > Application > Config`. + 2. Update `Name` to `"Game Name"`. + 3. Close the window. + 4. Open `main_menu_with_animations.tscn`. + 5. Select the `TitleLabel` node. + 6. The `Text` should match the project's name. + 1. If `Text` is customized, set `Auto Update` to false. + 7. Select the `SubtitleLabelNode` node and customize the `Text` as desired. + 8. Save the scene. + + +3. Add / remove configurable settings to / from menus. + + + 1. Open `[master|mini|audio|visual|input]_options_menu.tscn` scenes to edit their options. + 2. If an option is not desired, it can always be hidden, or removed entirely (sometimes with some additional work). + 3. If a new option is desired, refer to [Adding Custom Options.](/addons/maaacks_game_template/docs/AddingCustomOptions.md) + + +4. Update the game credits / attribution. + + + 1. Open `credits_label.tscn`. + 2. Update `CreditsLabel` with your desired text. BBCode allows for some formatting. + 3. Save the scene. + + +5. Continue with: + + 1. [Setting up the Main Menu.](/addons/maaacks_game_template/docs/MainMenuSetup.md) + 2. [Setting up a Game Scene.](/addons/maaacks_game_template/docs/GameSceneSetup.md) + 3. [Adding icons to the Input Options.](/addons/maaacks_game_template/docs/InputIconMapping.md) + 4. [Adding Custom Options.](/addons/maaacks_game_template/docs/AddingCustomOptions.md) diff --git a/addons/maaacks_game_template/docs/PluginSuite.md b/addons/maaacks_game_template/docs/PluginSuite.md new file mode 100644 index 0000000..c7f510d --- /dev/null +++ b/addons/maaacks_game_template/docs/PluginSuite.md @@ -0,0 +1,40 @@ +# Plugin Suite + +![Plugins Suite](../media/maaacks-plugin-suite-256x256.gif) + +Maaack's Game Templates are a culmination of a suite of plugins, that can be downloaded individually, if desired. + +## GitHub + +- [Game Template](https://github.com/Maaack/Godot-Game-Template) + - [Menus Template](https://github.com/Maaack/Godot-Menus-Template) + - [Options Menus](https://github.com/Maaack/Godot-Options-Menus) + - [Input Remapping](https://github.com/Maaack/Godot-Input-Remapping) + - [Scene Loader](https://github.com/Maaack/Godot-Scene-Loader) + - [Credits Scene](https://github.com/Maaack/Godot-Credits-Scene) + - [UI Sound Controller](https://github.com/Maaack/Godot-UI-Sound-Controller) + - [Music Controller](https://github.com/Maaack/Godot-Music-Controller) + +- [Minimal Game Template](https://github.com/Maaack/Godot-Minimal-Game-Template) + - [Options Menus](https://github.com/Maaack/Godot-Options-Menus) + - [Input Remapping](https://github.com/Maaack/Godot-Input-Remapping) + +## Godot Asset Library + +- [Game Template](https://godotengine.org/asset-library/asset/2709) + - [Menus Template](https://godotengine.org/asset-library/asset/2899) + - [Options Menus](https://godotengine.org/asset-library/asset/3058) + - [Input Remapping](https://godotengine.org/asset-library/asset/4051) + - [Scene Loader](https://godotengine.org/asset-library/asset/2896) + - [Credits Scene](https://godotengine.org/asset-library/asset/2932) + - [UI Sound Controller](https://godotengine.org/asset-library/asset/2897) + - [Music Controller](https://godotengine.org/asset-library/asset/2898) + +- [Minimal Game Template](https://godotengine.org/asset-library/asset/4657) + - [Options Menus](https://godotengine.org/asset-library/asset/3058) + - [Input Remapping](https://godotengine.org/asset-library/asset/4051) + +## YouTube Video + +[![All Plugins Video](https://img.youtube.com/vi/3yzaUSaROhw/hqdefault.jpg)](https://youtu.be/3yzaUSaROhw) + diff --git a/addons/maaacks_game_template/docs/Screenshots.md b/addons/maaacks_game_template/docs/Screenshots.md new file mode 100644 index 0000000..0d3bbc1 --- /dev/null +++ b/addons/maaacks_game_template/docs/Screenshots.md @@ -0,0 +1,86 @@ +# Screenshots + +1280x720 and 640x360 resolutions are shown, and resolutions up to 4k are supported. + +## 1280 X 720 + +![Main Menu](/addons/maaacks_game_template/media/screenshot-7-main-menu-1.png) +![Input List](/addons/maaacks_game_template/media/screenshot-7-input-remapping-1.png) +![Input List - Icons Filled](/addons/maaacks_game_template/media/screenshot-7-input-remapping-2.png) +![Input Tree - Icons Filled](/addons/maaacks_game_template/media/screenshot-7-input-remapping-3.png) +![Input List - Icons Outlined](/addons/maaacks_game_template/media/screenshot-7-input-remapping-4.png) +![Input Tree - Icons Outlined](/addons/maaacks_game_template/media/screenshot-7-input-remapping-5.png) +![Audio Options](/addons/maaacks_game_template/media/screenshot-7-audio-options-1.png) +![Video Options](/addons/maaacks_game_template/media/screenshot-7-video-options-1.png) +![Credits](/addons/maaacks_game_template/media/screenshot-7-credits-1.png) +![Credits](/addons/maaacks_game_template/media/screenshot-7-credits-2.png) +![Level Tutorial](/addons/maaacks_game_template/media/screenshot-7-tutorial-1.png) +![Level](/addons/maaacks_game_template/media/screenshot-7-level-1.png) +![Level](/addons/maaacks_game_template/media/screenshot-7-level-2.png) +![Level Won](/addons/maaacks_game_template/media/screenshot-7-level-won-1.png) +![Level Lost](/addons/maaacks_game_template/media/screenshot-7-level-lost-1.png) +![Game Won](/addons/maaacks_game_template/media/screenshot-7-game-won-1.png) +![End Credits](/addons/maaacks_game_template/media/screenshot-7-end-credits-1.png) +![End Credits](/addons/maaacks_game_template/media/screenshot-7-end-credits-3.png) +![End Credits](/addons/maaacks_game_template/media/screenshot-7-end-credits-4.png) +![Loading Screen - Loading](/addons/maaacks_game_template/media/screenshot-7-loading-screen-1.png) +![Loading Screen - Still Loading](/addons/maaacks_game_template/media/screenshot-7-loading-screen-3.png) +![Loading Screen - Stalled](/addons/maaacks_game_template/media/screenshot-7-loading-screen-4.png) +![Loading Screen - Complete](/addons/maaacks_game_template/media/screenshot-7-loading-screen-5.png) + +## 640 x 360 + +Screenshots organized by included themes. + +### Default (No Theme) +![Main Menu - Default](/addons/maaacks_game_template/media/screenshot-6-main-menu-5.png) +![Input List - Default](/addons/maaacks_game_template/media/screenshot-6-input-list-3.png) +![Input List - Default](/addons/maaacks_game_template/media/screenshot-6-input-list-2.png) +![Input List - Default](/addons/maaacks_game_template/media/screenshot-6-input-list-1.png) +![Input Tree - Default](/addons/maaacks_game_template/media/screenshot-6-input-tree-4.png) +![Audio Options - Default](/addons/maaacks_game_template/media/screenshot-6-audio-options-6.png) +![Video Options - Default](/addons/maaacks_game_template/media/screenshot-6-video-options-6.png) +![Level Won - Default](/addons/maaacks_game_template/media/screenshot-6-level-won-3.png) +![Level Lost - Default](/addons/maaacks_game_template/media/screenshot-6-level-lost-3.png) + +### Gravity +![Main Menu - Gravity](/addons/maaacks_game_template/media/screenshot-6-main-menu-1.png) +![Input List - Gravity](/addons/maaacks_game_template/media/screenshot-6-input-list-5.png) +![Input List - Gravity](/addons/maaacks_game_template/media/screenshot-6-input-list-4.png) +![Input Tree - Gravity](/addons/maaacks_game_template/media/screenshot-6-input-tree-1.png) +![Audio Options - Gravity](/addons/maaacks_game_template/media/screenshot-6-audio-options-1.png) +![Video Options - Gravity](/addons/maaacks_game_template/media/screenshot-6-video-options-1.png) +![Level State - Gravity](/addons/maaacks_game_template/media/screenshot-6-level-state-1.png) +![Pause Menu - Gravity](/addons/maaacks_game_template/media/screenshot-6-pause-menu-2.png) +![Level Won - Gravity](/addons/maaacks_game_template/media/screenshot-6-level-won-1.png) +![Level Lost - Gravity](/addons/maaacks_game_template/media/screenshot-6-level-lost-1.png) + +### Lore +![Main Menu - Lore](/addons/maaacks_game_template/media/screenshot-6-main-menu-2.png) +![Input List - Lore](/addons/maaacks_game_template/media/screenshot-6-input-list-6.png) +![Input List - Lore](/addons/maaacks_game_template/media/screenshot-6-input-list-7.png) +![Input Tree - Lore](/addons/maaacks_game_template/media/screenshot-6-input-tree-2.png) +![Audio Options - Lore](/addons/maaacks_game_template/media/screenshot-6-audio-options-3.png) +![Video Options - Lore](/addons/maaacks_game_template/media/screenshot-6-video-options-3.png) +![Pause Menu - Lore](/addons/maaacks_game_template/media/screenshot-6-pause-menu-3.png) + +### Steal This Theme +![Main Menu - Steal This Theme](/addons/maaacks_game_template/media/screenshot-6-main-menu-4.png) +![Input Tree - Steal This Theme](/addons/maaacks_game_template/media/screenshot-6-input-tree-5.png) +![Audio Options - Steal This Theme](/addons/maaacks_game_template/media/screenshot-6-audio-options-4.png) +![Video Options - Steal This Theme](/addons/maaacks_game_template/media/screenshot-6-video-options-5.png) +![Pause Menu - Steal This Theme](/addons/maaacks_game_template/media/screenshot-6-pause-menu-4.png) +![Level Won - Steal This Theme](/addons/maaacks_game_template/media/screenshot-6-level-won-2.png) +![Level Won - Steal This Theme](/addons/maaacks_game_template/media/screenshot-6-level-won-2.png) +![Loading Screen - Steal This Theme](/addons/maaacks_game_template/media/screenshot-6-loading-screen-1.png) +![Loading Screen - Steal This Theme](/addons/maaacks_game_template/media/screenshot-6-loading-screen-2.png) +![Loading Screen - Steal This Theme](/addons/maaacks_game_template/media/screenshot-6-loading-screen-3.png) +![Loading Screen - Steal This Theme](/addons/maaacks_game_template/media/screenshot-6-loading-screen-4.png) + +### Tower +![Main Menu - Tower](/addons/maaacks_game_template/media/screenshot-6-main-menu-3.png) +![Input List - Tower](/addons/maaacks_game_template/media/screenshot-6-input-list-8.png) +![Input List - Tower](/addons/maaacks_game_template/media/screenshot-6-input-list-9.png) +![Input Tree - Tower](/addons/maaacks_game_template/media/screenshot-6-input-tree-3.png) +![Audio Options - Tower](/addons/maaacks_game_template/media/screenshot-6-audio-options-5.png) +![Video Options - Tower](/addons/maaacks_game_template/media/screenshot-6-video-options-4.png) diff --git a/addons/maaacks_game_template/docs/UploadingToItchIo.md b/addons/maaacks_game_template/docs/UploadingToItchIo.md new file mode 100644 index 0000000..8c85240 --- /dev/null +++ b/addons/maaacks_game_template/docs/UploadingToItchIo.md @@ -0,0 +1,40 @@ +# Uploading to itch.io + +This is a guide on using _Butler_ along with a _Butler Manager_ helper script to rapidly upload and deploy your builds to itch.io. It's useful for game jams! + +## Butler + +_Butler_ is a command-line tool provided by itch.io to upload content to project pages on itch.io. + +Get it here: https://itchio.itch.io/butler + +After installing it, run `butler login` and go through the login flow. You should only have to do this once. + +_Butler_ automatically compares builds and only uploads what has changed, so the first upload will take the longest, but every upload after should be faster. + +## Exporting + +It is recommended to create an `exports/` directory for your builds, add the directory to your `.gitignore` file (if applicable), and also add a `.gdignore` file to the directory to avoid having Godot add `*.import` files to it as well. + +## Butler Manager + +This script provided at `addons/maaacks_game_template/extras/scripts/butler_manager.sh` can be used to rapidly deploy 4 different builds to your project page. Make sure you can run `bash` shell scripts on your OS. Copy the script into your `exports/` directory and mark it as an executable, if required. + +Run the script with `./butler_manager.sh`. On the first run, it will ask for the destination for uploads. This is a combination of the page owner and the project's URL. + +The Butler Manager will look for directories named the following: + +- HTML5 +- Linux +- Windows +- MacOS + +Matching directories will be uploaded by _Butler_ to their corresponding channels on itch.io. They will then be processed by itch.io servers and eventually appear on the page (usually within 2 minutes). + +The owner of the project page will also get a notification when the builds have finished processing. + +You can re-run `./butler_manager.sh` right after an export from Godot to keep your builds synced. + +## Automating export and publication + +You can use Github Actions to automate these steps. Look into the `.git/workflows` folder [and this guide](./BuildAndPublish.md). diff --git a/addons/maaacks_game_template/docs/Videos.md b/addons/maaacks_game_template/docs/Videos.md new file mode 100644 index 0000000..320ce44 --- /dev/null +++ b/addons/maaacks_game_template/docs/Videos.md @@ -0,0 +1,14 @@ +# Videos + +## Tutorials + +[![Quick Intro Video](https://img.youtube.com/vi/U9CB3vKINVw/hqdefault.jpg)](https://youtu.be/U9CB3vKINVw) +[![Installation Video](https://img.youtube.com/vi/-QWJnZ8bVdk/hqdefault.jpg)](https://youtu.be/-QWJnZ8bVdk) +[![1.0 Release Video](https://img.youtube.com/vi/DE_6kqvT_yc/hqdefault.jpg)](https://youtu.be/DE_6kqvT_yc) +[![All Plugins Video](https://img.youtube.com/vi/3yzaUSaROhw/hqdefault.jpg)](https://youtu.be/3yzaUSaROhw) +[![UI Theming (1) Video](https://img.youtube.com/vi/SBE4icfXYRA/hqdefault.jpg)](https://youtu.be/SBE4icfXYRA) +[![UI Theming (2) Video](https://img.youtube.com/vi/wCc2QUnaBKo/hqdefault.jpg)](https://youtu.be/wCc2QUnaBKo) + +## Events + +[![Lessons from 25+ Game Jams with Godot](https://img.youtube.com/vi/nUOAzSNmz1A/hqdefault.jpg)](https://youtu.be/nUOAzSNmz1A) diff --git a/addons/maaacks_game_template/extras/scripts/asset_checker.sh b/addons/maaacks_game_template/extras/scripts/asset_checker.sh new file mode 100644 index 0000000..4174191 --- /dev/null +++ b/addons/maaacks_game_template/extras/scripts/asset_checker.sh @@ -0,0 +1,70 @@ +#!/bin/bash +# asset checker command +# Used for quickly checking that assets (like audio files) are being used where expected. +# +# Recursively searches through scene files (.tscn, .scn, .res) +# for occurrences of asset types (default: AudioStream). +# It then outputs the paths of assets discovered, +# along with the file names that use them. + +short_flag=false +asset_type="AudioStream" + +print_usage() { + printf "Usage: -sa %s\n" "$asset_type" +} + +while getopts 'a:s' flag; do + case "${flag}" in + a) + asset_type="${OPTARG}" + ;; + s) + short_flag=true + ;; + *) + print_usage + exit 1 + ;; + esac +done + +# Initialize an associative array to store paths and corresponding files +declare -A path_files + +while IFS=: read -r file line; do + path=$(echo "$line" | grep -o 'path="[^"]*' | cut -d'"' -f2) + if [ -n "$path" ]; then + # Append the current file to the string of files for this path + # Note: Bash does not support having arrays as values of associative array. + # Using a pipe `|` separator instead, and then splitting on output + if [ -z "${path_files["$path"]}" ]; then + path_files["$path"]=$file + else + path_files["$path"]+="|$file" + fi + fi +done < <(egrep -ir --include=*.{tscn,scn,res} "type=\"$asset_type\"") + +# Get the paths and sort them +sorted_paths=() +for key in "${!path_files[@]}"; do + sorted_paths+=("$key") +done +IFS=$'\n' sorted_paths=($(sort <<< "${sorted_paths[*]}")) +unset IFS + +# Print out the results +for path in "${sorted_paths[@]}"; do + # Note: Bash does not support having arrays as values of associative array. + # Splitting the concatenated files string on the pipe `|` separator. + IFS='|' read -r -a files_array <<< "${path_files[$path]}" + files_count=${#files_array[@]} + printf "%-80s | Uses: %s\n" "$path" "$files_count" + if ! $short_flag ; then + for file in "${files_array[@]}"; do + printf "\t%82s\n" "$file" + done + echo + fi +done diff --git a/addons/maaacks_game_template/extras/scripts/build-and-publish.yml b/addons/maaacks_game_template/extras/scripts/build-and-publish.yml new file mode 100644 index 0000000..dfa36bd --- /dev/null +++ b/addons/maaacks_game_template/extras/scripts/build-and-publish.yml @@ -0,0 +1,281 @@ +# MIT License + +# Copyright (c) 2018 BARICHELLO + +# 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. + +# Original file: https://github.com/abarichello/godot-ci/blob/master/.github/workflows/godot-ci.yml +# This edited version is triggered using a Github Release and uploads artifacts to the release. +# Furthermore, a new job (upload to itch.io using butler) has been added. +# Modified by: Nicolas Oulianov (github @oulianov) & Marek Belski + +name: "Build and Deploy to Itch.io" + +on: + release: + types: [published] + workflow_dispatch: + +permissions: + # Allow release asset uploads + contents: write + +env: + # Set this repository variable to the version to build with (>=4) + # This will be used for all container images: barichello/godot-ci:[#.#] + # Look at available versions here: https://hub.docker.com/r/barichello/godot-ci/tags + GODOT_VERSION: ${{ vars.GODOT_VERSION }} + # Set this repository variable to your game name (it will be the filename downloaded) + EXPORT_NAME: ${{ vars.EXPORT_NAME }} + # NOTE: If your `project.godot` is at the repository root, set `PROJECT_PATH` to "." + # If it's in a subdirectory, set it to the subdirectory name (e.g., "your-game") + PROJECT_PATH: . + +jobs: + export-web: + name: Web Export + runs-on: ubuntu-24.04 + container: + image: barichello/godot-ci:${{ vars.GODOT_VERSION }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + lfs: true + + - name: Setup + run: | + mkdir -v -p ~/.local/share/godot/export_templates/ + mkdir -v -p ~/.config/ + mv /root/.config/godot ~/.config/godot || true + mv /root/.local/share/godot/export_templates/${GODOT_VERSION}.stable ~/.local/share/godot/export_templates/${GODOT_VERSION}.stable || true + + - name: Web Build + run: | + mkdir -v -p build/web + EXPORT_DIR="$(readlink -f build)" + cd $PROJECT_PATH + godot --headless --verbose --export-release "Web" "$EXPORT_DIR/web/index.html" + + - name: Prepare web release asset (zip) + run: | + # ensure zip is available in the container + if ! command -v zip >/dev/null 2>&1; then + apt-get update && apt-get install -y zip + fi + # Change to the web directory and zip its contents directly + cd build/web + zip -r ../${EXPORT_NAME}-web.zip . + ls -lh ../${EXPORT_NAME}-web.zip + shell: bash + + - name: Upload to GitHub Release (if this run is a release) + if: ${{ github.event_name == 'release' }} + uses: svenstaro/upload-release-action@v2 + with: + file: build/${{ env.EXPORT_NAME }}-web.zip + + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: web + path: build/web + + export-windows: + name: Windows Export + runs-on: ubuntu-24.04 + container: + image: barichello/godot-ci:${{ vars.GODOT_VERSION }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + lfs: true + + - name: Setup + run: | + mkdir -v -p ~/.local/share/godot/export_templates/ + mkdir -v -p ~/.config/ + mv /root/.config/godot ~/.config/godot || true + mv /root/.local/share/godot/export_templates/${GODOT_VERSION}.stable ~/.local/share/godot/export_templates/${GODOT_VERSION}.stable || true + + - name: Windows Build + run: | + mkdir -v -p build/windows + EXPORT_DIR="$(readlink -f build)" + cd $PROJECT_PATH + godot --headless --verbose --export-release "Windows Desktop" "$EXPORT_DIR/windows/${EXPORT_NAME}.exe" + + - name: Archive Build + run: | + # ensure zip is available in the container + if ! command -v zip >/dev/null 2>&1; then + apt-get update && apt-get install -y zip + fi + # Change to the web directory and zip its contents directly + cd build/windows + zip -r ../${EXPORT_NAME}-windows.zip . + ls -lh ../${EXPORT_NAME}-windows.zip + shell: bash + + - name: Upload to GitHub Release (if this run is a release) + if: ${{ github.event_name == 'release' }} + uses: svenstaro/upload-release-action@v2 + with: + file: build/${{ env.EXPORT_NAME }}-windows.zip + + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: windows + path: build/${{ env.EXPORT_NAME }}-windows.zip + + export-linux: + name: Linux Export + runs-on: ubuntu-24.04 + container: + image: barichello/godot-ci:${{ vars.GODOT_VERSION }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + lfs: true + + - name: Setup + run: | + mkdir -v -p ~/.local/share/godot/export_templates/ + mkdir -v -p ~/.config/ + mv /root/.config/godot ~/.config/godot || true + mv /root/.local/share/godot/export_templates/${GODOT_VERSION}.stable ~/.local/share/godot/export_templates/${GODOT_VERSION}.stable || true + + - name: Linux Build + run: | + mkdir -v -p build/linux + EXPORT_DIR="$(readlink -f build)" + cd $PROJECT_PATH + godot --headless --verbose --export-release "Linux" "$EXPORT_DIR/linux/${EXPORT_NAME}.x86_64" + + - name: Archive Build + run: | + # ensure zip is available in the container + if ! command -v zip >/dev/null 2>&1; then + apt-get update && apt-get install -y zip + fi + # Change to the web directory and zip its contents directly + cd build/linux + zip -r ../${EXPORT_NAME}-linux.zip . + ls -lh ../${EXPORT_NAME}-linux.zip + shell: bash + + - name: Upload to GitHub Release (if this run is a release) + if: ${{ github.event_name == 'release' }} + uses: svenstaro/upload-release-action@v2 + with: + file: build/${{ env.EXPORT_NAME }}-linux.zip + + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: linux + path: build/${{ env.EXPORT_NAME }}-linux.zip + + export-mac: + name: macOS Export + runs-on: ubuntu-24.04 + container: + image: barichello/godot-ci:${{ vars.GODOT_VERSION }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + lfs: true + + - name: Setup + run: | + mkdir -v -p ~/.local/share/godot/export_templates/ + mkdir -v -p ~/.config/ + mv /root/.config/godot ~/.config/godot || true + mv /root/.local/share/godot/export_templates/${GODOT_VERSION}.stable ~/.local/share/godot/export_templates/${GODOT_VERSION}.stable || true + + - name: Mac Build + run: | + mkdir -v -p build/mac + EXPORT_DIR="$(readlink -f build)" + cd $PROJECT_PATH + godot --headless --verbose --export-release "macOS" "$EXPORT_DIR/mac/${EXPORT_NAME}-mac.zip" + + - name: Upload to GitHub Release (if this run is a release) + if: ${{ github.event_name == 'release' }} + uses: svenstaro/upload-release-action@v2 + with: + file: build/mac/${{ env.EXPORT_NAME }}-mac.zip + + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: mac + path: build/mac + + deploy-to-itch: + name: Deploy to Itch.io + needs: [export-web, export-windows, export-linux, export-mac] + runs-on: ubuntu-24.04 + env: + BUTLER_API_KEY: ${{ secrets.BUTLER_API_KEY }} # Create this SECRET on Github + ITCH_USERNAME: ${{ vars.ITCH_USERNAME }} # Create this VARIABLE on Github + ITCH_GAME: ${{ vars.ITCH_GAME }} # Create this VARIABLE on Github + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: builds + + - name: Install Butler + run: | + curl -L -o butler.zip https://broth.itch.zone/butler/linux-amd64/LATEST/archive/default + unzip butler.zip + chmod +x butler + ./butler -V + + - name: Get version from tag or set default + id: version + run: | + if [ "${{ github.event_name }}" = "release" ]; then + VERSION="${{ github.event.release.tag_name }}" + else + VERSION="dev-${{ github.run_number }}" + fi + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "Deploying version: $VERSION" + + - name: Upload to Itch.io - Web + run: | + ./butler push builds/web ${{ env.ITCH_USERNAME }}/${{ env.ITCH_GAME }}:html5 --userversion "${{ steps.version.outputs.version }}" + + - name: Upload to Itch.io - Windows + run: | + ./butler push builds/windows ${{ env.ITCH_USERNAME }}/${{ env.ITCH_GAME }}:windows --userversion "${{ steps.version.outputs.version }}" + + - name: Upload to Itch.io - Linux + run: | + ./butler push builds/linux ${{ env.ITCH_USERNAME }}/${{ env.ITCH_GAME }}:linux --userversion "${{ steps.version.outputs.version }}" + + - name: Upload to Itch.io - macOS + run: | + ./butler push builds/mac ${{ env.ITCH_USERNAME }}/${{ env.ITCH_GAME }}:mac --userversion "${{ steps.version.outputs.version }}" diff --git a/addons/maaacks_game_template/extras/scripts/butler_manager.sh b/addons/maaacks_game_template/extras/scripts/butler_manager.sh new file mode 100644 index 0000000..b576487 --- /dev/null +++ b/addons/maaacks_game_template/extras/scripts/butler_manager.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# butler manager command +# Uploads directories as builds to matching itch.io channels. +# HTML5 => html5 +# Linux => linux +# Windows => win +# MacOS => osx + +file=upload_destination.txt +directories=("HTML5" "Linux" "Windows" "MacOS") +channels=("html5" "linux" "win" "osx") + +# Check if the file exists +if [ ! -e $file ]; then + # File doesn't exist, create an empty one + touch $file +fi + +# File exists, read the first line into a variable +read -r destination < $file + +if [ -z "$destination" ]; then + # File is empty, prompt the user for input + echo "Please enter the build destination (username/project-url-after-slash)." + read -r user_input + + # Save user input to the file + echo "$user_input" > "$file" + echo "Destination saved to $file." + destination="$user_input" +fi + +# Check for the existence of directories and upload contents +for ((i=0; i<${#directories[@]}; i++)); do + dir="${directories[i]}" + channel="${channels[i]}" + + if [ -d "$dir" ]; then + echo butler push ./$dir/ $destination:$channel + butler push ./$dir/ $destination:$channel + else + echo "Directory '$dir' does not exist." + fi +done \ No newline at end of file diff --git a/addons/maaacks_game_template/extras/scripts/capture_mouse.gd b/addons/maaacks_game_template/extras/scripts/capture_mouse.gd new file mode 100644 index 0000000..cf6ce5b --- /dev/null +++ b/addons/maaacks_game_template/extras/scripts/capture_mouse.gd @@ -0,0 +1,8 @@ +extends Control +## Control node that captures the mouse for games that require it. +## +## Used for games that use the mouse to move the camera (ex. FPS or third-person shooters). + +func _gui_input(event): + if event is InputEventMouseButton and Input.mouse_mode != Input.MOUSE_MODE_CAPTURED: + Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) diff --git a/addons/maaacks_game_template/extras/scripts/capture_mouse.gd.uid b/addons/maaacks_game_template/extras/scripts/capture_mouse.gd.uid new file mode 100644 index 0000000..e3b0ff7 --- /dev/null +++ b/addons/maaacks_game_template/extras/scripts/capture_mouse.gd.uid @@ -0,0 +1 @@ +uid://dqdyrkm3jily6 diff --git a/addons/maaacks_game_template/extras/scripts/level_loader.gd b/addons/maaacks_game_template/extras/scripts/level_loader.gd new file mode 100644 index 0000000..b87cd3b --- /dev/null +++ b/addons/maaacks_game_template/extras/scripts/level_loader.gd @@ -0,0 +1,51 @@ +@tool +class_name LevelLoader +extends Node +## Loads scenes into a container. + +signal level_load_started +signal level_loaded +signal level_ready + +## Container where the level instance will be added. +@export var level_container : Node +## Optional reference to a loading screen in the scene. +## Requires Maaack's Scene Loader. +@export var level_loading_screen : Node +@export_group("Debugging") +@export var current_level : Node + +## If Maaack's Scene Loader is installed, then it will be used to change scenes. +@onready var scene_loader_node = get_tree().root.get_node_or_null(^"SceneLoader") + +var is_loading : bool = false + +func _attach_level(level_resource : Resource): + assert(level_container != null, "level_container is null") + var instance = level_resource.instantiate() + level_container.call_deferred("add_child", instance) + return instance + +func load_level(level_path : String): + if is_loading : return + if is_instance_valid(current_level): + current_level.queue_free() + await current_level.tree_exited + current_level = null + if scene_loader_node: + is_loading = true + scene_loader_node.load_scene(level_path, true) + if level_loading_screen: + level_loading_screen.reset() + level_load_started.emit() + await scene_loader_node.scene_loaded + is_loading = false + current_level = _attach_level(scene_loader_node.get_resource()) + if level_loading_screen: + level_loading_screen.close() + else: + var level_scene = load(level_path) + current_level = _attach_level(level_scene) + level_loaded.emit() + await current_level.ready + level_ready.emit() diff --git a/addons/maaacks_game_template/extras/scripts/level_loader.gd.uid b/addons/maaacks_game_template/extras/scripts/level_loader.gd.uid new file mode 100644 index 0000000..d3c169c --- /dev/null +++ b/addons/maaacks_game_template/extras/scripts/level_loader.gd.uid @@ -0,0 +1 @@ +uid://bbymrin0cm704 diff --git a/addons/maaacks_game_template/extras/scripts/level_manager.gd b/addons/maaacks_game_template/extras/scripts/level_manager.gd new file mode 100644 index 0000000..019aadc --- /dev/null +++ b/addons/maaacks_game_template/extras/scripts/level_manager.gd @@ -0,0 +1,183 @@ +class_name LevelManager +extends Node +## Manage level changes in games. +## +## A helper script to assign to a node in a scene. +## It works with a level loader and can open menus when players win or lose. +## It can either be assigned a starting level path or a scene lister. +## It can detect signals from levels to change levels in an open-world. +## With a scene lister, it will instead traverse through levels linearly. + +## Required reference to a level loader in the scene. +@export var level_loader : LevelLoader +## Optional path to a starting level scene. +## Required if there is no scene lister. +@export_file var starting_level_path : String +## Optional reference to a scene lister in the scene. +## Required if there is no starting level path. +@export var scene_lister : SceneLister +## Whether to load the starting level when ready. +@export var auto_load : bool = true +@export_group("Scenes") +## Path to a main menu scene. +@export_file("*.tscn") var main_menu_scene_path : String +## Optional path to an ending scene. +@export_file("*.tscn") var ending_scene_path : String +## Optional screen to be shown after the game is won. +@export var game_won_scene : PackedScene +## Optional screen to be shown after the level is lost. +@export var level_lost_scene : PackedScene +## Optional screen to be shown after the level is won. +@export var level_won_scene : PackedScene + +## If Maaack's Scene Loader is installed, then it will be used to change scenes. +@onready var scene_loader_node = get_tree().root.get_node_or_null(^"SceneLoader") + +## Reference to the current level node. +var current_level : Node +var current_level_path : String : set = set_current_level_path +var checkpoint_level_path : String : set = set_checkpoint_level_path + +func set_current_level_path(value : String) -> void: + current_level_path = value + +func set_checkpoint_level_path(value : String) -> void: + checkpoint_level_path = value + +func _try_connecting_signal_to_node(node : Node, signal_name : String, callable : Callable) -> void: + if node.has_signal(signal_name) and not node.is_connected(signal_name, callable): + node.connect(signal_name, callable) + +func _try_connecting_signal_to_level(signal_name : String, callable : Callable) -> void: + _try_connecting_signal_to_node(current_level, signal_name, callable) + +func get_main_menu_scene_path() -> String: + return main_menu_scene_path + +func _load_main_menu() -> void: + if scene_loader_node: + scene_loader_node.load_scene(get_main_menu_scene_path()) + else: + get_tree().change_scene_to_file(get_main_menu_scene_path()) + +func _find_in_scene_lister(level_path : String) -> int: + if not scene_lister: return -1 + level_path = ResourceUID.ensure_path(level_path) + return scene_lister.files.find(level_path) + +func is_on_last_level() -> bool: + var current_level_id = _find_in_scene_lister(current_level_path) + return current_level_id > -1 and current_level_id == scene_lister.files.size() - 1 + +func get_relative_level_path(offset : int = 1) -> String: + var current_level_id := _find_in_scene_lister(current_level_path) + if current_level_id > -1: + if current_level_id >= max(0, -(offset)) and current_level_id < scene_lister.files.size() - max(0, offset): + current_level_id += offset + return scene_lister.files[current_level_id] + return "" + +func get_next_level_path() -> String: + return get_relative_level_path(1) + +func get_prev_level_path() -> String: + return get_relative_level_path(-1) + +func get_ending_scene_path() -> String: + return ending_scene_path + +func _load_ending() -> void: + if not get_ending_scene_path().is_empty(): + if scene_loader_node: + scene_loader_node.load_scene(get_ending_scene_path()) + else: + get_tree().change_scene_to_file(get_ending_scene_path()) + else: + _load_main_menu() + +func _on_level_lost() -> void: + if level_lost_scene: + var instance = level_lost_scene.instantiate() + get_tree().current_scene.add_child(instance) + _try_connecting_signal_to_node(instance, &"restart_pressed", _reload_level) + _try_connecting_signal_to_node(instance, &"main_menu_pressed", _load_main_menu) + else: + _reload_level() + +func get_checkpoint_level_path() -> String: + if checkpoint_level_path.is_empty(): + if scene_lister: + return scene_lister.files.front() + if not starting_level_path.is_empty(): + return starting_level_path + return checkpoint_level_path + +func load_level(level_path : String) -> void: + current_level_path = level_path + level_loader.load_level(level_path) + +func _load_checkpoint_level() -> void: + load_level(get_checkpoint_level_path()) + +func _reload_level() -> void: + load_level(current_level_path) + +func _load_win_screen_or_ending() -> void: + if game_won_scene: + var instance = game_won_scene.instantiate() + get_tree().current_scene.add_child(instance) + _try_connecting_signal_to_node(instance, &"continue_pressed", _load_ending) + _try_connecting_signal_to_node(instance, &"restart_pressed", _reload_level) + _try_connecting_signal_to_node(instance, &"main_menu_pressed", _load_main_menu) + else: + _load_ending() + +func _load_level_won_screen_or_checkpoint() -> void: + if level_won_scene: + var instance = level_won_scene.instantiate() + get_tree().current_scene.add_child(instance) + _try_connecting_signal_to_node(instance, &"continue_pressed", _load_checkpoint_level) + _try_connecting_signal_to_node(instance, &"restart_pressed", _reload_level) + _try_connecting_signal_to_node(instance, &"main_menu_pressed", _load_main_menu) + else: + _load_checkpoint_level() + +func _on_level_won(next_level_path : String = ""): + if next_level_path.is_empty(): + next_level_path = get_next_level_path() + if next_level_path.is_empty(): + _load_win_screen_or_ending() + else: + checkpoint_level_path = next_level_path + _load_level_won_screen_or_checkpoint() + +func _on_level_changed(next_level_path : String): + checkpoint_level_path = next_level_path + _load_checkpoint_level() + +func _connect_level_signals() -> void: + _try_connecting_signal_to_level(&"level_lost", _on_level_lost) + _try_connecting_signal_to_level(&"level_won", _on_level_won) + _try_connecting_signal_to_level(&"level_changed", _on_level_changed) + +func _on_level_loader_level_loaded() -> void: + current_level = level_loader.current_level + await current_level.ready + _connect_level_signals() + +func _on_level_loader_level_load_started() -> void: + pass + +func _on_level_loader_level_ready() -> void: + pass + +func _auto_load() -> void: + if auto_load: + _load_checkpoint_level() + +func _ready() -> void: + if Engine.is_editor_hint(): return + level_loader.level_loaded.connect(_on_level_loader_level_loaded) + level_loader.level_ready.connect(_on_level_loader_level_ready) + level_loader.level_load_started.connect(_on_level_loader_level_load_started) + _auto_load() diff --git a/addons/maaacks_game_template/extras/scripts/level_manager.gd.uid b/addons/maaacks_game_template/extras/scripts/level_manager.gd.uid new file mode 100644 index 0000000..67c0b4c --- /dev/null +++ b/addons/maaacks_game_template/extras/scripts/level_manager.gd.uid @@ -0,0 +1 @@ +uid://bllg4gg7v1tsr diff --git a/addons/maaacks_game_template/extras/scripts/scene_lister.gd b/addons/maaacks_game_template/extras/scripts/scene_lister.gd new file mode 100644 index 0000000..c2bb4c4 --- /dev/null +++ b/addons/maaacks_game_template/extras/scripts/scene_lister.gd @@ -0,0 +1,23 @@ +@tool +extends Node +class_name SceneLister +## Helper class for listing all the scenes in a directory. + +## List of paths to scene files. +## Prefilled in the editor by selecting a directory. +@export var files : Array[String] +## Prefill files with any scenes in the directory. +@export_dir var directory : String : + set(value): + directory = value + _refresh_files() + +func _refresh_files(): + if not is_inside_tree() or directory.is_empty(): return + var dir_access = DirAccess.open(directory) + if dir_access: + files.clear() + for file in dir_access.get_files(): + if not file.ends_with(".tscn"): + continue + files.append(directory + "/" + file) diff --git a/addons/maaacks_game_template/extras/scripts/scene_lister.gd.uid b/addons/maaacks_game_template/extras/scripts/scene_lister.gd.uid new file mode 100644 index 0000000..ae7cd7f --- /dev/null +++ b/addons/maaacks_game_template/extras/scripts/scene_lister.gd.uid @@ -0,0 +1 @@ +uid://wjq7li836lwj diff --git a/addons/maaacks_game_template/extras/scripts/win_lose_manager.gd b/addons/maaacks_game_template/extras/scripts/win_lose_manager.gd new file mode 100644 index 0000000..51f268d --- /dev/null +++ b/addons/maaacks_game_template/extras/scripts/win_lose_manager.gd @@ -0,0 +1,73 @@ +extends Node + +## Path to a main menu scene. +@export_file("*.tscn") var main_menu_scene_path : String +## Optional path to an ending scene. +@export_file("*.tscn") var ending_scene_path : String +## Optional screen to be shown after the game is won. +@export var game_won_scene : PackedScene +## Optional screen to be shown after the game is lost. +@export var game_lost_scene : PackedScene + +## If Maaack's Scene Loader is installed, then it will be used to change scenes. +@onready var scene_loader_node = get_tree().root.get_node_or_null(^"SceneLoader") + +var has_lost_game : bool = false +var has_won_game : bool = false + +func _try_connecting_signal_to_node(node : Node, signal_name : String, callable : Callable) -> void: + if node.has_signal(signal_name) and not node.is_connected(signal_name, callable): + node.connect(signal_name, callable) + +func get_main_menu_scene_path() -> String: + return main_menu_scene_path + +func _load_main_menu() -> void: + if scene_loader_node: + scene_loader_node.load_scene(get_main_menu_scene_path()) + else: + get_tree().change_scene_to_file(get_main_menu_scene_path()) + +func get_ending_scene_path() -> String: + return ending_scene_path + +func _load_ending() -> void: + if not get_ending_scene_path().is_empty(): + if scene_loader_node: + scene_loader_node.load_scene(get_ending_scene_path()) + else: + get_tree().change_scene_to_file(get_ending_scene_path()) + else: + _load_main_menu() + +func _load_lose_screen_or_reload() -> void: + if game_lost_scene: + var instance = game_lost_scene.instantiate() + get_tree().current_scene.add_child(instance) + _try_connecting_signal_to_node(instance, &"restart_pressed", _reload_level) + _try_connecting_signal_to_node(instance, &"main_menu_pressed", _load_main_menu) + else: + _reload_level() + +func _reload_level() -> void: + get_tree().reload_current_scene() + +func _load_win_screen_or_ending() -> void: + if game_won_scene: + var instance = game_won_scene.instantiate() + get_tree().current_scene.add_child(instance) + _try_connecting_signal_to_node(instance, &"continue_pressed", _load_ending) + _try_connecting_signal_to_node(instance, &"restart_pressed", _reload_level) + _try_connecting_signal_to_node(instance, &"main_menu_pressed", _load_main_menu) + else: + _load_ending() + +func game_lost() -> void: + if has_won_game or has_lost_game: return + has_lost_game = true + _load_lose_screen_or_reload() + +func game_won() -> void: + if has_won_game or has_lost_game: return + has_won_game = true + _load_win_screen_or_ending() diff --git a/addons/maaacks_game_template/extras/scripts/win_lose_manager.gd.uid b/addons/maaacks_game_template/extras/scripts/win_lose_manager.gd.uid new file mode 100644 index 0000000..32aa4d2 --- /dev/null +++ b/addons/maaacks_game_template/extras/scripts/win_lose_manager.gd.uid @@ -0,0 +1 @@ +uid://bmlwpkrav3q56 diff --git a/addons/maaacks_game_template/installer/check_plugin_version.gd b/addons/maaacks_game_template/installer/check_plugin_version.gd new file mode 100644 index 0000000..a867401 --- /dev/null +++ b/addons/maaacks_game_template/installer/check_plugin_version.gd @@ -0,0 +1,86 @@ +@tool +extends Node +## Script for comparing the version of a plugin to the latest release on GitHub. + +signal new_version_detected(version: String) +signal versions_matched +signal failed + +const APIClient = MaaacksGameTemplatePlugin.APIClient + +const API_RELEASES_URL := "https://api.github.com/repos/%s/%s/releases" + +## The directory of the plugin to update. Typically in res://addons/. +@export var plugin_directory : String +## The URL of the GitHub repo to pull new releases. +@export var plugin_github_url : String : + set(value): + plugin_github_url = value + _update_urls() +@export_group("Advanced") +## If true, automatically check for a new version when ready. +@export var auto_start : bool = false +## Text to remove from the tag before comparing versions. +@export var replace_tag_name : String = "v" +## The default lowest version to display. +@export var default_version : String = "0.0.0" +## If true, test comparing versions. +## Replace with @export_tool_button for Godot 4.4+ +@export var _test_action : bool = false : + set(value): + if value and Engine.is_editor_hint(): + compare_versions() + +@onready var _api_client : APIClient = $APIClient + +var _zipball_url : String + +func get_plugin_version() -> String : + if not plugin_directory.is_empty(): + for enabled_plugin in ProjectSettings.get_setting("editor_plugins/enabled"): + if enabled_plugin.contains(plugin_directory): + var config := ConfigFile.new() + var error = config.load(enabled_plugin) + if error != OK: + return default_version + return config.get_value("plugin", "version", default_version) + return default_version + +func _update_urls() -> void: + if plugin_github_url.is_empty(): return + if _api_client == null: return + var regex := RegEx.create_from_string("https:\\/\\/github\\.com\\/([\\w-]+)\\/([\\w-]+)\\/*") + var regex_match := regex.search(plugin_github_url) + if regex_match == null: return + var username := regex_match.get_string(1) + var repository := regex_match.get_string(2) + _api_client.api_url = API_RELEASES_URL % [username, repository] + +func _on_api_client_request_failed(error) -> void: + failed.emit() + queue_free() + +func _on_api_client_response_received(response_body) -> void: + if response_body is not Array or response_body.is_empty(): + failed.emit() + queue_free() + return + var latest_release : Dictionary = response_body.front() + var tag_name := default_version + if latest_release.has("tag_name"): + tag_name = latest_release["tag_name"] + if replace_tag_name: + tag_name = tag_name.replacen(replace_tag_name, "") + var current_tag_name = get_plugin_version() + if tag_name != current_tag_name: + new_version_detected.emit(tag_name) + else: + versions_matched.emit() + queue_free() + +func compare_versions() -> void: + _api_client.request() + +func _ready() -> void: + if auto_start: + compare_versions() diff --git a/addons/maaacks_game_template/installer/check_plugin_version.gd.uid b/addons/maaacks_game_template/installer/check_plugin_version.gd.uid new file mode 100644 index 0000000..f07d5ac --- /dev/null +++ b/addons/maaacks_game_template/installer/check_plugin_version.gd.uid @@ -0,0 +1 @@ +uid://chjaeg7rfnixu diff --git a/addons/maaacks_game_template/installer/check_plugin_version.tscn b/addons/maaacks_game_template/installer/check_plugin_version.tscn new file mode 100644 index 0000000..7c70f52 --- /dev/null +++ b/addons/maaacks_game_template/installer/check_plugin_version.tscn @@ -0,0 +1,16 @@ +[gd_scene load_steps=3 format=3 uid="uid://depfw7i463ojc"] + +[ext_resource type="Script" uid="uid://chjaeg7rfnixu" path="res://addons/maaacks_game_template/installer/check_plugin_version.gd" id="1_aqelj"] +[ext_resource type="PackedScene" uid="uid://cf0hkngq1mgfy" path="res://addons/maaacks_game_template/utilities/api_client.tscn" id="2_5myc0"] + +[node name="CheckPluginVersion" type="Node"] +script = ExtResource("1_aqelj") +plugin_directory = "maaacks_game_template" +plugin_github_url = "https://github.com/Maaack/Godot-Minimal-Game-Template" + +[node name="APIClient" parent="." instance=ExtResource("2_5myc0")] +api_url = "https://api.github.com/repos/Maaack/Godot-Minimal-Game-Template/releases" +request_method = 0 + +[connection signal="request_failed" from="APIClient" to="." method="_on_api_client_request_failed"] +[connection signal="response_received" from="APIClient" to="." method="_on_api_client_response_received"] diff --git a/addons/maaacks_game_template/installer/copy_and_edit_files.gd b/addons/maaacks_game_template/installer/copy_and_edit_files.gd new file mode 100644 index 0000000..714595f --- /dev/null +++ b/addons/maaacks_game_template/installer/copy_and_edit_files.gd @@ -0,0 +1,175 @@ +@tool +extends Node +## Script for automatically copying Godot scenes and scripts without UIDs. + +signal canceled +signal completed(target_path : String) + +const UID_PREG_MATCH = r'uid="uid:\/\/[0-9a-z]+" ' +const RUNNING_CHECK_DELAY : float = 0.25 +const RESAVING_DELAY : float = 1.0 +const RAW_COPY_EXTENSIONS : Array = ["gd", "md", "txt"] +const OMIT_COPY_EXTENSIONS : Array = ["uid"] +const REPLACE_CONTENT_EXTENSIONS : Array = ["gd", "tscn", "tres", "md"] + +@onready var destination_dialog : FileDialog = $DestinationDialog + +@export_dir var relative_path : String : + set(value): + relative_path = value + if not relative_path.ends_with("/"): + relative_path += "/" +@export var replace_strings_map : Dictionary +@export var visible : bool = true : + set(value): + visible = value + if is_inside_tree(): + destination_dialog.visible = visible + +func show() -> void: + visible = true + +func hide() -> void: + visible = false + +func close() -> void: + queue_free() + +func _remove_uids(content : String) -> String: + var regex = RegEx.new() + regex.compile(UID_PREG_MATCH) + return regex.sub(content, "", true) + +func _replace_paths(content : String, target_path : String) -> String: + return content.replace(relative_path.trim_prefix("res://"), target_path.trim_prefix("res://")) + +func _replace_strings(content : String) -> String: + for key in replace_strings_map: + var value : String = replace_strings_map[key] + content = content.replace(key, value) + return content + +func _replace_content(content : String, target_path : String) -> String: + var replaced_content : String + replaced_content = _remove_uids(content) + replaced_content = _replace_paths(replaced_content, target_path) + replaced_content = _replace_strings(replaced_content) + return replaced_content + +func _replace_file_contents(file_path : String, target_path : String) -> void: + var extension : String = file_path.get_extension() + if extension not in REPLACE_CONTENT_EXTENSIONS: + return + var file = FileAccess.open(file_path, FileAccess.READ) + if file == null: + push_error("plugin error - null file: `%s`" % file_path) + return + var original_content = file.get_as_text() + file.close() + var replaced_content := _replace_content(original_content, target_path) + if replaced_content == original_content: return + file = FileAccess.open(file_path, FileAccess.WRITE) + file.store_string(replaced_content) + file.close() + +func _save_resource(resource_path : String, resource_destination : String, whitelisted_extensions : PackedStringArray = []) -> Error: + var extension : String = resource_path.get_extension() + if whitelisted_extensions.size() > 0: + if not extension in whitelisted_extensions: + return OK + if extension == "import": + # skip import files + return OK + var file_object = load(resource_path) + if file_object is Resource: + var possible_extensions = ResourceSaver.get_recognized_extensions(file_object) + if possible_extensions.has(extension): + return ResourceSaver.save(file_object, resource_destination, ResourceSaver.FLAG_CHANGE_PATH) + else: + return ERR_FILE_UNRECOGNIZED + else: + return ERR_FILE_UNRECOGNIZED + return OK + +func _raw_copy_file_path(file_path : String, destination_path : String) -> Error: + var dir := DirAccess.open("res://") + var error := dir.copy(file_path, destination_path) + return error + +func _copy_file_path(file_path : String, destination_path : String, target_path : String) -> Error: + var error : Error + if file_path.get_extension() in OMIT_COPY_EXTENSIONS: + return error + if file_path.get_extension() in RAW_COPY_EXTENSIONS: + error = _raw_copy_file_path(file_path, destination_path) + else: + error = _save_resource(file_path, destination_path) + if error == ERR_FILE_UNRECOGNIZED: + error = _raw_copy_file_path(file_path, destination_path) + if not error: + _replace_file_contents(destination_path, target_path) + return error + +func _copy_directory_path(dir_path : String, target_path : String) -> void: + if not dir_path.ends_with("/"): + dir_path += "/" + var dir = DirAccess.open(dir_path) + if dir: + dir.list_dir_begin() + var file_name = dir.get_next() + var error : Error + while file_name != "" and error == 0: + var file_relative_path = dir_path.trim_prefix(relative_path) + var destination_path = target_path + file_relative_path + file_name + var full_file_path = dir_path + file_name + if dir.current_is_dir(): + if not dir.dir_exists(destination_path): + error = dir.make_dir(destination_path) + _copy_directory_path(full_file_path, target_path) + else: + error = _copy_file_path(full_file_path, destination_path, target_path) + file_name = dir.get_next() + if error: + push_error("plugin error - copying path: %s" % error) + else: + push_error("plugin error - accessing path: %s" % dir_path) + +func _complete(target_path : String) -> void: + completed.emit(target_path) + close() + +func _wait_for_scan_and_complete(target_path : String) -> void: + var timer: Timer = Timer.new() + var callable := func(): + if EditorInterface.get_resource_filesystem().is_scanning(): return + timer.stop() + _complete(target_path) + timer.queue_free() + timer.timeout.connect(callable) + add_child(timer) + timer.start(RUNNING_CHECK_DELAY) + +func _delayed_saving_and_next_prompt(target_path : String) -> void: + var timer: Timer = Timer.new() + var callable := func(): + timer.stop() + EditorInterface.save_all_scenes() + EditorInterface.get_resource_filesystem().scan() + _wait_for_scan_and_complete(target_path) + timer.queue_free() + timer.timeout.connect(callable) + add_child(timer) + timer.start(RESAVING_DELAY) + +func _copy_to_directory(target_path : String) -> void: + if not target_path.ends_with("/"): + target_path += "/" + _copy_directory_path(relative_path, target_path) + _delayed_saving_and_next_prompt(target_path) + +func _on_destination_dialog_dir_selected(dir): + _copy_to_directory(dir) + +func _on_destination_dialog_canceled(): + canceled.emit() + close() diff --git a/addons/maaacks_game_template/installer/copy_and_edit_files.gd.uid b/addons/maaacks_game_template/installer/copy_and_edit_files.gd.uid new file mode 100644 index 0000000..f3e72db --- /dev/null +++ b/addons/maaacks_game_template/installer/copy_and_edit_files.gd.uid @@ -0,0 +1 @@ +uid://terojwgplakq diff --git a/addons/maaacks_game_template/installer/copy_and_edit_files.tscn b/addons/maaacks_game_template/installer/copy_and_edit_files.tscn new file mode 100644 index 0000000..b526944 --- /dev/null +++ b/addons/maaacks_game_template/installer/copy_and_edit_files.tscn @@ -0,0 +1,16 @@ +[gd_scene load_steps=3 format=3 uid="uid://6dpw3bj32ov"] + +[ext_resource type="Script" uid="uid://terojwgplakq" path="res://addons/maaacks_game_template/installer/copy_and_edit_files.gd" id="1_oyky1"] +[ext_resource type="PackedScene" uid="uid://doa4t485iflon" path="res://addons/maaacks_game_template/installer/destination_dialog.tscn" id="2_g35hh"] + +[node name="CopyAndEditFiles" type="Node"] +script = ExtResource("1_oyky1") +relative_path = "res://addons/maaacks_game_template/examples/" +replace_strings_map = { +"StateExample": "State" +} + +[node name="DestinationDialog" parent="." instance=ExtResource("2_g35hh")] + +[connection signal="canceled" from="DestinationDialog" to="." method="_on_destination_dialog_canceled"] +[connection signal="dir_selected" from="DestinationDialog" to="." method="_on_destination_dialog_dir_selected"] diff --git a/addons/maaacks_game_template/installer/copy_confirmation_dialog.tscn b/addons/maaacks_game_template/installer/copy_confirmation_dialog.tscn new file mode 100644 index 0000000..f6aece2 --- /dev/null +++ b/addons/maaacks_game_template/installer/copy_confirmation_dialog.tscn @@ -0,0 +1,15 @@ +[gd_scene format=3 uid="uid://byupuycr28u17"] + +[node name="CopyConfirmationDialog" type="ConfirmationDialog"] +oversampling_override = 1.0 +title = "Copy Examples" +initial_position = 2 +size = Vector2i(1024, 148) +visible = true +exclusive = false +ok_button_text = "Yes" +dialog_text = "Plugin enabled. It is recommended to copy the example scenes to a destination outside of the addons/ folder before editing them. + +Would you like to copy the examples now?" +dialog_autowrap = true +cancel_button_text = "No" diff --git a/addons/maaacks_game_template/installer/delete_examples_confirmation_dialog.tscn b/addons/maaacks_game_template/installer/delete_examples_confirmation_dialog.tscn new file mode 100644 index 0000000..c17116e --- /dev/null +++ b/addons/maaacks_game_template/installer/delete_examples_confirmation_dialog.tscn @@ -0,0 +1,16 @@ +[gd_scene format=3 uid="uid://n2pm5se13638"] + +[node name="DeleteExamplesConfirmationDialog" type="ConfirmationDialog"] +oversampling_override = 1.0 +title = "Delete Source Examples" +initial_position = 2 +size = Vector2i(1024, 256) +visible = true +ok_button_text = "Yes" +dialog_text = "If the copied scenes work as expected, you may delete the source examples folder. This avoids confusing both developers and the Godot editor. + +This will also remove the option to copy the examples again. However, one copy is enough for most use cases. + +Would you like to delete the source examples folder now?" +dialog_autowrap = true +cancel_button_text = "No" diff --git a/addons/maaacks_game_template/installer/delete_examples_short_confirmation_dialog.tscn b/addons/maaacks_game_template/installer/delete_examples_short_confirmation_dialog.tscn new file mode 100644 index 0000000..a00fcd9 --- /dev/null +++ b/addons/maaacks_game_template/installer/delete_examples_short_confirmation_dialog.tscn @@ -0,0 +1,17 @@ +[gd_scene format=3 uid="uid://2ubjcuhlam2o"] + +[node name="DeleteExamplesShortConfirmationDialog" type="ConfirmationDialog"] +oversampling_override = 1.0 +title = "Delete Source Examples" +initial_position = 2 +size = Vector2i(1024, 300) +visible = true +ok_button_text = "Yes" +dialog_text = "This will delete the original examples folder from the plugin (inside of addons/). + +Copies of the examples (outside of addons/) will not be affected. + +Are you sure you would like to delete the examples folder? +" +dialog_autowrap = true +cancel_button_text = "No" diff --git a/addons/maaacks_game_template/installer/destination_dialog.tscn b/addons/maaacks_game_template/installer/destination_dialog.tscn new file mode 100644 index 0000000..4b2be54 --- /dev/null +++ b/addons/maaacks_game_template/installer/destination_dialog.tscn @@ -0,0 +1,12 @@ +[gd_scene format=3 uid="uid://doa4t485iflon"] + +[node name="DestinationDialog" type="FileDialog"] +oversampling_override = 1.0 +title = "Select a Destination" +initial_position = 2 +size = Vector2i(1024, 640) +visible = true +exclusive = false +ok_button_text = "Select Current Folder" +mode_overrides_title = false +file_mode = 2 diff --git a/addons/maaacks_game_template/installer/kenney_input_prompts_dialog.gd b/addons/maaacks_game_template/installer/kenney_input_prompts_dialog.gd new file mode 100644 index 0000000..4d79818 --- /dev/null +++ b/addons/maaacks_game_template/installer/kenney_input_prompts_dialog.gd @@ -0,0 +1,12 @@ +@tool +extends ConfirmationDialog + +const SHORT_DESCRIPTION : String = "Choose a style for icons in the input remapping menu. This style can be changed later." + +signal configuration_selected(index : int) + +func _on_item_list_item_selected(index) -> void: + configuration_selected.emit(index) + +func set_short_description() -> void: + %Label.text = SHORT_DESCRIPTION diff --git a/addons/maaacks_game_template/installer/kenney_input_prompts_dialog.gd.uid b/addons/maaacks_game_template/installer/kenney_input_prompts_dialog.gd.uid new file mode 100644 index 0000000..d797498 --- /dev/null +++ b/addons/maaacks_game_template/installer/kenney_input_prompts_dialog.gd.uid @@ -0,0 +1 @@ +uid://c8bnydo2t6vsu diff --git a/addons/maaacks_game_template/installer/kenney_input_prompts_dialog.tscn b/addons/maaacks_game_template/installer/kenney_input_prompts_dialog.tscn new file mode 100644 index 0000000..26b6308 --- /dev/null +++ b/addons/maaacks_game_template/installer/kenney_input_prompts_dialog.tscn @@ -0,0 +1,84 @@ +[gd_scene load_steps=14 format=3 uid="uid://vr4wkvk734wg"] + +[ext_resource type="Script" uid="uid://c8bnydo2t6vsu" path="res://addons/maaacks_game_template/installer/kenney_input_prompts_dialog.gd" id="1_nf1bc"] +[ext_resource type="Texture2D" uid="uid://dxvbsbs428nj7" path="res://addons/maaacks_game_template/assets/input-icons/icons-filled-colored.png" id="2_0nqam"] +[ext_resource type="Texture2D" uid="uid://5gjytq6nlww3" path="res://addons/maaacks_game_template/assets/input-icons/icons-filled-white.png" id="3_ynuxh"] +[ext_resource type="Texture2D" uid="uid://cj4f8dma2647n" path="res://addons/maaacks_game_template/assets/input-icons/icons-outlined-colored.png" id="4_dqbfh"] +[ext_resource type="Texture2D" uid="uid://cj524k2rdjvjo" path="res://addons/maaacks_game_template/assets/input-icons/icons-outlined-white.png" id="5_1tkva"] +[ext_resource type="Texture2D" uid="uid://nqdhl41h0tpv" path="res://addons/maaacks_game_template/assets/input-icons/icons-filled-colored-2x.png" id="6_r3yyh"] +[ext_resource type="Texture2D" uid="uid://df0qisbxrhsqa" path="res://addons/maaacks_game_template/assets/input-icons/icons-filled-white-2x.png" id="7_xgp8o"] +[ext_resource type="Texture2D" uid="uid://1gr33hl6p8tu" path="res://addons/maaacks_game_template/assets/input-icons/icons-outlined-colored-2x.png" id="8_ag5dy"] +[ext_resource type="Texture2D" uid="uid://betu06qbgx5gf" path="res://addons/maaacks_game_template/assets/input-icons/icons-outlined-white-2x.png" id="9_3b8mx"] +[ext_resource type="Texture2D" uid="uid://cp7xvu3oujlnj" path="res://addons/maaacks_game_template/assets/input-icons/icons-filled-colored-vector.svg" id="10_ag5dy"] +[ext_resource type="Texture2D" uid="uid://dl5p5qw3hp8y" path="res://addons/maaacks_game_template/assets/input-icons/icons-filled-white-vector.svg" id="11_3b8mx"] +[ext_resource type="Texture2D" uid="uid://2xcu58kisr45" path="res://addons/maaacks_game_template/assets/input-icons/icons-outlined-colored-vector.svg" id="12_rrkvx"] +[ext_resource type="Texture2D" uid="uid://cf4s4f7tvltpp" path="res://addons/maaacks_game_template/assets/input-icons/icons-outlined-white-vector.svg" id="13_bkfjd"] + +[node name="KenneyInputPromptsDialog" type="ConfirmationDialog"] +oversampling_override = 1.0 +title = "Add Kenney Input Prompts Pack" +initial_position = 2 +size = Vector2i(1024, 640) +visible = true +ok_button_text = "Yes" +dialog_autowrap = true +cancel_button_text = "No" +script = ExtResource("1_nf1bc") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +custom_minimum_size = Vector2(560, 443) +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 +theme_override_constants/separation = 16 + +[node name="Label" type="Label" parent="VBoxContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(384, 0) +layout_mode = 2 +text = "Would you like to install Kenney's Input Prompts? + +This adds icons for a majority of input keys and devices in the input remapping menu. They are Creative Commons Zero (CC0) licensed, about 3.9 MB in size (7.6 MB with *.import files), and get installed into the assets folder. + +Choose a style for icons in the input remapping menu. The style can be changed later." +autowrap_mode = 3 + +[node name="ItemList" type="ItemList" parent="VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +item_count = 12 +same_column_width = true +item_0/text = "Filled and Colored Vector" +item_0/icon = ExtResource("10_ag5dy") +item_1/text = "Filled and White Vector" +item_1/icon = ExtResource("11_3b8mx") +item_2/text = "Outlined and Colored Vector" +item_2/icon = ExtResource("12_rrkvx") +item_3/text = "Outlined and White Vector" +item_3/icon = ExtResource("13_bkfjd") +item_4/text = "Filled and Colored 64x64" +item_4/icon = ExtResource("2_0nqam") +item_5/text = "Filled and White 64x64" +item_5/icon = ExtResource("3_ynuxh") +item_6/text = "Outlined and Colored 64x64" +item_6/icon = ExtResource("4_dqbfh") +item_7/text = "Outlined and White 64x64" +item_7/icon = ExtResource("5_1tkva") +item_8/text = "Filled and Colored 128x128" +item_8/icon = ExtResource("6_r3yyh") +item_9/text = "Filled and White 128x128" +item_9/icon = ExtResource("7_xgp8o") +item_10/text = "Outlined and Colored 128x128" +item_10/icon = ExtResource("8_ag5dy") +item_11/text = "Outlined and White 128x128" +item_11/icon = ExtResource("9_3b8mx") + +[connection signal="item_selected" from="VBoxContainer/ItemList" to="." method="_on_item_list_item_selected"] diff --git a/addons/maaacks_game_template/installer/kenney_input_prompts_installer.gd b/addons/maaacks_game_template/installer/kenney_input_prompts_installer.gd new file mode 100644 index 0000000..50d1c8b --- /dev/null +++ b/addons/maaacks_game_template/installer/kenney_input_prompts_installer.gd @@ -0,0 +1,336 @@ +@tool +## Tool for installing icons and setting up the configuration of the input icon mapper. +extends Node + +## Sent when the user selects to cancel the installation process. +signal canceled +## Sent when the installation process has completed. +signal completed + +const DownloadAndExtract = MaaacksGameTemplatePlugin.DownloadAndExtract +const RELATIVE_PATH_TO_CONFIGURE_SCENE = "scenes/menus/options_menu/input/input_icon_mapper.tscn" +const REIMPORT_CHECK_DELAY : float = 0.5 +const OPEN_SCENE_DELAY : float = 0.5 +const MATCH_REGEX = """(\\[node name="InputIconMapper" instance=ExtResource\\("[0-9a-z_]+"\\)\\])[\\s\\S]*""" + +const FILLED_WHITE_CONFIGURATION = """ +replace_strings = { +"Capslock": "Caps Lock", +"Generic Stick": "Generic Left Stick", +"Guide": "Home", +"Slash Back": "Back Slash", +"Slash Forward": "Slash", +"Stick L": "Left Stick", +"Stick R": "Right Stick", +"Trigger L 1": "Left Shoulder", +"Trigger L 2": "Left Trigger", +"Trigger R 1": "Right Shoulder", +"Trigger R 2": "Right Trigger" +} +filtered_strings = Array[String](["keyboard", "color", "button", "arrow"]) +directories = Array[String](["res://assets/kenney_input-prompts/Keyboard & Mouse/Default", "res://assets/kenney_input-prompts/Generic/Default", "res://assets/kenney_input-prompts/Xbox Series/Default", "res://assets/kenney_input-prompts/PlayStation Series/Default", "res://assets/kenney_input-prompts/Nintendo Switch/Default", "res://assets/kenney_input-prompts/Steam Deck/Default"]) +filter = "color" +ends_with = ".png" +not_ends_with = "outline.png" +""" +const FILLED_COLOR_CONFIGURATION = """ +prioritized_strings = Array[String](["color"]) +replace_strings = { +"Capslock": "Caps Lock", +"Generic Stick": "Generic Left Stick", +"Guide": "Home", +"Slash Back": "Back Slash", +"Slash Forward": "Slash", +"Stick L": "Left Stick", +"Stick R": "Right Stick", +"Trigger L 1": "Left Shoulder", +"Trigger L 2": "Left Trigger", +"Trigger R 1": "Right Shoulder", +"Trigger R 2": "Right Trigger" +} +filtered_strings = Array[String](["keyboard", "color", "button", "arrow"]) +directories = Array[String](["res://assets/kenney_input-prompts/Keyboard & Mouse/Default", "res://assets/kenney_input-prompts/Generic/Default", "res://assets/kenney_input-prompts/Xbox Series/Default", "res://assets/kenney_input-prompts/PlayStation Series/Default", "res://assets/kenney_input-prompts/Nintendo Switch/Default", "res://assets/kenney_input-prompts/Steam Deck/Default"]) +ends_with = ".png" +not_ends_with = "outline.png" +""" +const OUTLINED_WHITE_CONFIGURATION = """ +prioritized_strings = Array[String](["outline"]) +replace_strings = { +"Capslock": "Caps Lock", +"Generic Stick": "Generic Left Stick", +"Guide": "Home", +"Slash Back": "Back Slash", +"Slash Forward": "Slash", +"Stick L": "Left Stick", +"Stick R": "Right Stick", +"Trigger L 1": "Left Shoulder", +"Trigger L 2": "Left Trigger", +"Trigger R 1": "Right Shoulder", +"Trigger R 2": "Right Trigger" +} +filtered_strings = Array[String](["keyboard", "color", "button", "arrow", "outline"]) +directories = Array[String](["res://assets/kenney_input-prompts/Keyboard & Mouse/Default", "res://assets/kenney_input-prompts/Generic/Default", "res://assets/kenney_input-prompts/Xbox Series/Default", "res://assets/kenney_input-prompts/PlayStation Series/Default", "res://assets/kenney_input-prompts/Nintendo Switch/Default", "res://assets/kenney_input-prompts/Steam Deck/Default"]) +filter = "color" +ends_with = ".png" +""" +const OUTLINED_COLOR_CONFIGURATION = """ +prioritized_strings = Array[String](["outline", "color"]) +replace_strings = { +"Capslock": "Caps Lock", +"Generic Stick": "Generic Left Stick", +"Guide": "Home", +"Slash Back": "Back Slash", +"Slash Forward": "Slash", +"Stick L": "Left Stick", +"Stick R": "Right Stick", +"Trigger L 1": "Left Shoulder", +"Trigger L 2": "Left Trigger", +"Trigger R 1": "Right Shoulder", +"Trigger R 2": "Right Trigger" +} +filtered_strings = Array[String](["keyboard", "color", "button", "arrow", "outline"]) +directories = Array[String](["res://assets/kenney_input-prompts/Keyboard & Mouse/Default", "res://assets/kenney_input-prompts/Generic/Default", "res://assets/kenney_input-prompts/Xbox Series/Default", "res://assets/kenney_input-prompts/PlayStation Series/Default", "res://assets/kenney_input-prompts/Nintendo Switch/Default", "res://assets/kenney_input-prompts/Steam Deck/Default"]) +ends_with = ".png" +""" + +const PACKAGE_EXTRA_DIRECTORIES := [ + "Flairs", + "Nintendo Gamecube", + "Nintendo Switch 2", + "Nintendo Wii", + "Nintendo WiiU", + "Playdate", + "Steam Controller", + "Touch", +] + +const PACKAGE_EXTRA_FILES := [ + "Preview", +] + +## Path start where the project examples have been copied. +@export_dir var copy_dir_path : String +## Path end where the zipped files are to be extracted. +@export var extract_extension : String + +@onready var _download_and_extract_node : DownloadAndExtract = $DownloadAndExtract +@onready var _skip_installation_dialog : ConfirmationDialog = $SkipInstallationDialog +@onready var _kenney_input_prompts_dialog : ConfirmationDialog = $KenneyInputPromptsDialog +@onready var _installing_dialog : AcceptDialog = $InstallingDialog +@onready var _clean_up_dialog : ConfirmationDialog = $CleanUpDialog +@onready var _error_dialog : AcceptDialog = $ErrorDialog +@onready var _stage_label : Label = %StageLabel +@onready var _progress_bar : ProgressBar = %ProgressBar + +var _configuration_index : int = -1 +## State flag of whether the tool is waiting for the filesystem to finish scanning. +var scanning : bool = false +## State flag for whether the tool is waiting for the filesystem to finish reimporting. +var reimporting : bool = false +## Flag for whether the tool will force a download and extraction, even if the contents exist. +var force : bool = false + +func _download_and_extract() -> void: + _installing_dialog.show() + _download_and_extract_node.run.call_deferred() + +func _run_complete() -> void: + completed.emit() + queue_free() + +func _clean_up_or_complete() -> void: + if _has_extras(): + _clean_up_dialog.show() + else: + _run_complete() + +func _process(_delta : float) -> void: + if _installing_dialog.visible: + _progress_bar.value = _download_and_extract_node.get_progress() + match _download_and_extract_node.stage: + DownloadAndExtract.DownloadAndExtractStage.DOWNLOAD: + _stage_label.text = "Downloading..." + DownloadAndExtract.DownloadAndExtractStage.SAVE: + _stage_label.text = "Saving..." + DownloadAndExtract.DownloadAndExtractStage.EXTRACT: + _stage_label.text = "Extracting..." + DownloadAndExtract.DownloadAndExtractStage.DELETE: + _stage_label.text = "Cleaning up..." + DownloadAndExtract.DownloadAndExtractStage.NONE: + _installing_dialog.hide() + elif scanning: + var file_system := EditorInterface.get_resource_filesystem() + if not file_system.is_scanning(): + scanning = false + await get_tree().create_timer(REIMPORT_CHECK_DELAY).timeout + if reimporting: + await file_system.resources_reimported + reimporting = false + _configure_and_complete() + +func _delete_recursive(path : String) -> void: + if not path.ends_with("/"): + path += "/" + var dir_access := DirAccess.open(path) + if dir_access == null: + return + var directories := dir_access.get_directories() + for directory in directories: + _delete_recursive(path + directory) + DirAccess.remove_absolute(path + directory) + var files := dir_access.get_files() + for file in files: + DirAccess.remove_absolute(path + file) + +func get_full_path() -> String: + var full_path := copy_dir_path + if not full_path.ends_with("/"): + full_path += "/" + full_path += extract_extension + if not full_path.ends_with("/"): + full_path += "/" + return full_path + +func _has_extras() -> bool: + var full_path := get_full_path() + var directories := DirAccess.get_directories_at(full_path) + for directory in directories: + for key in PACKAGE_EXTRA_DIRECTORIES: + if directory.contains(key): + return true + var files := DirAccess.get_files_at(full_path) + for file in files: + for key in PACKAGE_EXTRA_FILES: + if file.contains(key): + return true + return false + +func _delete_extras() -> void: + var full_path := get_full_path() + var directories := DirAccess.get_directories_at(full_path) + for directory in directories: + for key in PACKAGE_EXTRA_DIRECTORIES: + if directory.contains(key): + _delete_recursive(full_path + directory) + DirAccess.remove_absolute(full_path + directory) + continue + var files := DirAccess.get_files_at(full_path) + for file in files: + for key in PACKAGE_EXTRA_FILES: + if file.contains(key): + DirAccess.remove_absolute(full_path + file) + continue + EditorInterface.get_resource_filesystem().scan() + +func _configure_icons() -> void: + var input_mapper_path := copy_dir_path + RELATIVE_PATH_TO_CONFIGURE_SCENE + var icon_mapper_string := FileAccess.get_file_as_string(input_mapper_path) + var replacing_string := "$1\n" + match(_configuration_index % 4): + 0: + replacing_string += FILLED_COLOR_CONFIGURATION + 1: + replacing_string += FILLED_WHITE_CONFIGURATION + 2: + replacing_string += OUTLINED_COLOR_CONFIGURATION + 3: + replacing_string += OUTLINED_WHITE_CONFIGURATION + match(_configuration_index / 4): + 0: + replacing_string = replacing_string.replace("Default", "Vector").replace(".png", ".svg") + 1: + pass + 2: + replacing_string = replacing_string.replace("Default", "Double") + var regex = RegEx.new() + regex.compile(MATCH_REGEX) + icon_mapper_string = regex.sub(icon_mapper_string, replacing_string) + var file_rewrite := FileAccess.open(input_mapper_path, FileAccess.WRITE) + file_rewrite.store_string(icon_mapper_string) + file_rewrite.close() + if input_mapper_path in EditorInterface.get_open_scenes(): + EditorInterface.reload_scene_from_path(input_mapper_path) + else: + EditorInterface.open_scene_from_path(input_mapper_path) + await get_tree().create_timer(OPEN_SCENE_DELAY).timeout + EditorInterface.save_scene() + await get_tree().create_timer(REIMPORT_CHECK_DELAY).timeout + _clean_up_or_complete() + +func _configure_and_complete() -> void: + if _configuration_index >= 0: + _configure_icons() + return + _clean_up_or_complete() + +func _scan_filesystem_and_reimport() -> void: + var file_system := EditorInterface.get_resource_filesystem() + file_system.scan() + scanning = true + await file_system.resources_reimporting + reimporting = true + +func _enable_forced_install() -> void: + force = true + _download_and_extract_node.force = true + _kenney_input_prompts_dialog.show.call_deferred() + +func _enable_skipped_install() -> void: + _kenney_input_prompts_dialog.set_short_description() + _kenney_input_prompts_dialog.show.call_deferred() + +func _show_error_dialog(error : String) -> void: + _installing_dialog.hide() + _error_dialog.show() + _error_dialog.dialog_text = "%s!" % error + +func _ready() -> void: + _skip_installation_dialog.hide() + _kenney_input_prompts_dialog.hide() + _installing_dialog.hide() + _installing_dialog.get_ok_button().hide() + _clean_up_dialog.hide() + _error_dialog.hide() + _download_and_extract_node.extract_path = get_full_path() + if _download_and_extract_node.extract_path_exists(): + _skip_installation_dialog.show() + else: + _kenney_input_prompts_dialog.show() + +func _on_kenney_input_prompts_dialog_canceled() -> void: + canceled.emit() + queue_free() + +func _on_kenney_input_prompts_dialog_configuration_selected(index: int) -> void: + _configuration_index = index + +func _on_kenney_input_prompts_dialog_confirmed() -> void: + if _download_and_extract_node.extract_path_exists() and not force: + _configure_and_complete() + return + _download_and_extract() + +func _on_skip_installation_dialog_canceled() -> void: + _enable_forced_install() + +func _on_skip_installation_dialog_confirmed() -> void: + _enable_skipped_install() + +func _on_error_dialog_confirmed() -> void: + queue_free() + +func _on_error_dialog_canceled() -> void: + queue_free() + +func _on_download_and_extract_run_completed() -> void: + _scan_filesystem_and_reimport() + +func _on_download_and_extract_run_failed(error : String) -> void: + _show_error_dialog(error) + +func _on_clean_up_dialog_confirmed() -> void: + _delete_extras() + _run_complete() + +func _on_clean_up_dialog_canceled() -> void: + _run_complete() diff --git a/addons/maaacks_game_template/installer/kenney_input_prompts_installer.gd.uid b/addons/maaacks_game_template/installer/kenney_input_prompts_installer.gd.uid new file mode 100644 index 0000000..c4019d5 --- /dev/null +++ b/addons/maaacks_game_template/installer/kenney_input_prompts_installer.gd.uid @@ -0,0 +1 @@ +uid://cl2gk653d5d6o diff --git a/addons/maaacks_game_template/installer/kenney_input_prompts_installer.tscn b/addons/maaacks_game_template/installer/kenney_input_prompts_installer.tscn new file mode 100644 index 0000000..d9d5552 --- /dev/null +++ b/addons/maaacks_game_template/installer/kenney_input_prompts_installer.tscn @@ -0,0 +1,92 @@ +[gd_scene load_steps=4 format=3 uid="uid://c56u7kqt1l8yp"] + +[ext_resource type="Script" uid="uid://cl2gk653d5d6o" path="res://addons/maaacks_game_template/installer/kenney_input_prompts_installer.gd" id="1_ebstj"] +[ext_resource type="PackedScene" uid="uid://vr4wkvk734wg" path="res://addons/maaacks_game_template/installer/kenney_input_prompts_dialog.tscn" id="1_pslk0"] +[ext_resource type="PackedScene" uid="uid://5hhnbgqjwnic" path="res://addons/maaacks_game_template/utilities/download_and_extract.tscn" id="3_ebstj"] + +[node name="KenneyInputPromptsInstaller" type="Node"] +script = ExtResource("1_ebstj") +copy_dir_path = "res://" +extract_extension = "assets/kenney_input-prompts" + +[node name="DownloadAndExtract" parent="." instance=ExtResource("3_ebstj")] +zip_url = "https://github.com/Maaack/Kenney-Input-Prompts/archive/refs/tags/1.3.zip" +extract_path = "res://assets/kenney_input-prompts/" +skip_base_zip_dir = true +zip_file_path = "res://kenney_input-prompts.zip" +metadata/_custom_type_script = "uid://bkno1by7i3hrb" + +[node name="SkipInstallationDialog" type="ConfirmationDialog" parent="."] +title = "Skip Installation?" +initial_position = 2 +size = Vector2i(682, 160) +ok_button_text = "Skip" +dialog_text = "The input prompts pack appears to already be installed. + +Do you want to force a reinstall of the pack, or skip to picking a style?" +cancel_button_text = "Reinstall" + +[node name="KenneyInputPromptsDialog" parent="." instance=ExtResource("1_pslk0")] +visible = false + +[node name="InstallingDialog" type="AcceptDialog" parent="."] +title = "Installing..." +initial_position = 2 +size = Vector2i(400, 100) + +[node name="MarginContainer" type="MarginContainer" parent="InstallingDialog"] +offset_left = 4.0 +offset_top = 4.0 +offset_right = 396.0 +offset_bottom = 96.0 +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="VBoxContainer" type="VBoxContainer" parent="InstallingDialog/MarginContainer"] +layout_mode = 2 +alignment = 1 + +[node name="StageLabel" type="Label" parent="InstallingDialog/MarginContainer/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="ProgressBar" type="ProgressBar" parent="InstallingDialog/MarginContainer/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +max_value = 1.0 +step = 0.001 + +[node name="CleanUpDialog" type="ConfirmationDialog" parent="."] +auto_translate_mode = 1 +title = "Clean Up Extra Content?" +initial_position = 2 +size = Vector2i(1024, 210) +ok_button_text = "Yes" +dialog_text = "Kenney's Input Prompts contains extra content not used by the input remapping menu. + +This includes icons for devices not currently detected and preview images. Removing the extras cuts the total size of extracted assets by almost 50%. The option to change input icon styles will remain available after the clean up, too. + +Would you like to have the extra content removed?" +dialog_autowrap = true +cancel_button_text = "No" + +[node name="ErrorDialog" type="AcceptDialog" parent="."] +title = "Error!" +initial_position = 2 +size = Vector2i(400, 128) + +[connection signal="run_completed" from="DownloadAndExtract" to="." method="_on_download_and_extract_run_completed"] +[connection signal="run_failed" from="DownloadAndExtract" to="." method="_on_download_and_extract_run_failed"] +[connection signal="canceled" from="SkipInstallationDialog" to="." method="_on_skip_installation_dialog_canceled"] +[connection signal="confirmed" from="SkipInstallationDialog" to="." method="_on_skip_installation_dialog_confirmed"] +[connection signal="canceled" from="KenneyInputPromptsDialog" to="." method="_on_kenney_input_prompts_dialog_canceled"] +[connection signal="configuration_selected" from="KenneyInputPromptsDialog" to="." method="_on_kenney_input_prompts_dialog_configuration_selected"] +[connection signal="confirmed" from="KenneyInputPromptsDialog" to="." method="_on_kenney_input_prompts_dialog_confirmed"] +[connection signal="canceled" from="CleanUpDialog" to="." method="_on_clean_up_dialog_canceled"] +[connection signal="confirmed" from="CleanUpDialog" to="." method="_on_clean_up_dialog_confirmed"] +[connection signal="canceled" from="ErrorDialog" to="." method="_on_error_dialog_canceled"] +[connection signal="confirmed" from="ErrorDialog" to="." method="_on_error_dialog_confirmed"] diff --git a/addons/maaacks_game_template/installer/main_scene_confirmation_dialog.gd b/addons/maaacks_game_template/installer/main_scene_confirmation_dialog.gd new file mode 100644 index 0000000..327d9aa --- /dev/null +++ b/addons/maaacks_game_template/installer/main_scene_confirmation_dialog.gd @@ -0,0 +1,8 @@ +@tool +extends ConfirmationDialog + +const MAIN_SCENE_UPDATE_TEXT = "Would you like to update the project's main scene?\n\nCurrent:\n%s\n\nNew:\n%s\n" + +func set_main_scene_text(new_scene_path): + var current_scene_path : String = ProjectSettings.get_setting("application/run/main_scene", "") + dialog_text = MAIN_SCENE_UPDATE_TEXT % [current_scene_path, new_scene_path] diff --git a/addons/maaacks_game_template/installer/main_scene_confirmation_dialog.gd.uid b/addons/maaacks_game_template/installer/main_scene_confirmation_dialog.gd.uid new file mode 100644 index 0000000..19830df --- /dev/null +++ b/addons/maaacks_game_template/installer/main_scene_confirmation_dialog.gd.uid @@ -0,0 +1 @@ +uid://exfk51fr7yx0 diff --git a/addons/maaacks_game_template/installer/main_scene_confirmation_dialog.tscn b/addons/maaacks_game_template/installer/main_scene_confirmation_dialog.tscn new file mode 100644 index 0000000..626e877 --- /dev/null +++ b/addons/maaacks_game_template/installer/main_scene_confirmation_dialog.tscn @@ -0,0 +1,18 @@ +[gd_scene load_steps=2 format=3 uid="uid://gblkpm37lq1j"] + +[ext_resource type="Script" uid="uid://exfk51fr7yx0" path="res://addons/maaacks_game_template/installer/main_scene_confirmation_dialog.gd" id="1_1ydgd"] + +[node name="MainSceneConfirmationDialog" type="ConfirmationDialog"] +oversampling_override = 1.0 +title = "Update Main Scene" +initial_position = 2 +size = Vector2i(1024, 192) +visible = true +exclusive = false +ok_button_text = "Yes" +dialog_text = "Would you like to update the project's main scene? + +" +dialog_autowrap = true +cancel_button_text = "No" +script = ExtResource("1_1ydgd") diff --git a/addons/maaacks_game_template/installer/override.cfg b/addons/maaacks_game_template/installer/override.cfg new file mode 100644 index 0000000..8ca7535 --- /dev/null +++ b/addons/maaacks_game_template/installer/override.cfg @@ -0,0 +1,36 @@ +; Project settings override file. +; Adds gamepad inputs to built-in actions. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + + +[input] + +ui_accept={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194309,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194310,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":32,"physical_keycode":0,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":0,"pressure":0.0,"pressed":true,"script":null) +] +} +ui_cancel={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194305,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":6,"pressure":0.0,"pressed":true,"script":null) +] +} +ui_page_up={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194323,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":9,"pressure":0.0,"pressed":true,"script":null) +] +} +ui_page_down={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194324,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":10,"pressure":0.0,"pressed":true,"script":null) +] +} diff --git a/addons/maaacks_game_template/installer/play_opening_confirmation_dialog.tscn b/addons/maaacks_game_template/installer/play_opening_confirmation_dialog.tscn new file mode 100644 index 0000000..d3054de --- /dev/null +++ b/addons/maaacks_game_template/installer/play_opening_confirmation_dialog.tscn @@ -0,0 +1,14 @@ +[gd_scene format=3 uid="uid://cukfpfjy3827p"] + +[node name="PlayOpeningConfirmationDialog" type="ConfirmationDialog"] +oversampling_override = 1.0 +title = "Run & Test" +initial_position = 2 +size = Vector2i(1024, 148) +visible = true +ok_button_text = "Yes" +dialog_text = "It is recommended to run the opening scene of the plugin and test if any issues occurred during the copying process. + +Would you like to run and test the scenes now?" +dialog_autowrap = true +cancel_button_text = "No" diff --git a/addons/maaacks_game_template/installer/release_notes_label.gd b/addons/maaacks_game_template/installer/release_notes_label.gd new file mode 100644 index 0000000..dafbda8 --- /dev/null +++ b/addons/maaacks_game_template/installer/release_notes_label.gd @@ -0,0 +1,79 @@ +@tool +extends RichTextLabel + +const HEADING_STRING_REPLACEMENT = "$1[font_size=%d]$2[/font_size]" +const BOLD_HEADING_STRING_REPLACEMENT = "$1[b][font_size=%d]$2[/font_size][/b]" + +@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 var h5_font_size: int +@export var h6_font_size: int +@export var bold_headings : bool +@export_group("Extra Options") +@export var disable_images : bool = false +@export var disable_urls : bool = false +@export var disable_bolds : bool = false + +func regex_replace_imgs(markdown_text:String) -> String: + var regex = RegEx.new() + var match_string := "" + var replace_string := "" + if not disable_images: + replace_string = "$1" + regex.compile(match_string) + return regex.sub(markdown_text, replace_string, true) + +func regex_replace_urls(markdown_text:String) -> String: + var regex = RegEx.new() + var match_string := "(https:\\/\\/.*)($|\\s)" + var replace_string := "$1" + if not disable_urls: + replace_string = "[url=$1]$1[/url]" + regex.compile(match_string) + return regex.sub(markdown_text, replace_string, true) + +func regex_replace_bolds(markdown_text:String) -> String: + var regex = RegEx.new() + var match_string := "\\*\\*(.*)\\*\\*" + var replace_string := "$1" + if not disable_bolds: + replace_string = "[b]$1[/b]" + regex.compile(match_string) + return regex.sub(markdown_text, replace_string, true) + +func regex_replace_titles(markdown_text:String) -> String: + var iter = 0 + var heading_font_sizes : Array[int] = [ + h1_font_size, + h2_font_size, + h3_font_size, + h4_font_size, + h5_font_size, + h6_font_size] + for heading_font_size in heading_font_sizes: + iter += 1 + var regex = RegEx.new() + var match_string : String = "([^#]|^)#{%d}\\s([^\n]*)" % iter + var replace_string := HEADING_STRING_REPLACEMENT % [heading_font_size] + if bold_headings: + replace_string = BOLD_HEADING_STRING_REPLACEMENT % [heading_font_size] + regex.compile(match_string) + markdown_text = regex.sub(markdown_text, replace_string, true) + return markdown_text + +func from_release_notes(markdown_text : String) -> void: + markdown_text = regex_replace_imgs(markdown_text) + markdown_text = regex_replace_urls(markdown_text) + markdown_text = regex_replace_bolds(markdown_text) + markdown_text = regex_replace_titles(markdown_text) + text = markdown_text + +func _on_meta_clicked(meta: String) -> void: + if meta.begins_with("https://"): + var _err = OS.shell_open(meta) + +func _ready() -> void: + meta_clicked.connect(_on_meta_clicked) diff --git a/addons/maaacks_game_template/installer/release_notes_label.gd.uid b/addons/maaacks_game_template/installer/release_notes_label.gd.uid new file mode 100644 index 0000000..8b7e565 --- /dev/null +++ b/addons/maaacks_game_template/installer/release_notes_label.gd.uid @@ -0,0 +1 @@ +uid://cgv1xnmh7vr66 diff --git a/addons/maaacks_game_template/installer/setup_complete_dialog.tscn b/addons/maaacks_game_template/installer/setup_complete_dialog.tscn new file mode 100644 index 0000000..edfa299 --- /dev/null +++ b/addons/maaacks_game_template/installer/setup_complete_dialog.tscn @@ -0,0 +1,14 @@ +[gd_scene format=3 uid="uid://bie1wby7a56or"] + +[node name="SetupCompleteDialog" type="AcceptDialog"] +oversampling_override = 1.0 +title = "Setup Complete!" +initial_position = 2 +size = Vector2i(1032, 360) +visible = true +dialog_text = "Thanks for installing Maaack's Minimal Game Template! + +Go to `Project > Tools > Run Maaack's Minimal Game Template Setup...` for extra features. +Each step of the setup can also be revisited. + +See the included README for next steps and additional documentation." diff --git a/addons/maaacks_game_template/installer/setup_wizard.gd b/addons/maaacks_game_template/installer/setup_wizard.gd new file mode 100644 index 0000000..c8fba4b --- /dev/null +++ b/addons/maaacks_game_template/installer/setup_wizard.gd @@ -0,0 +1,125 @@ +@tool +extends AcceptDialog + +@export_file("*.tscn") var check_version_scene_path : String +@export_dir var input_prompts_directory_path : String + +@onready var plugin_label : Label = %PluginLabel +@onready var update_label : Label = %UpdateLabel +@onready var update_check_box : CheckBox = %UpdateCheckBox +@onready var update_button : Button = %UpdateButton +@onready var copy_check_box : CheckBox = %CopyCheckBox +@onready var copy_button : Button = %CopyButton +@onready var delete_check_box : CheckBox = %DeleteCheckBox +@onready var delete_button : Button = %DeleteButton +@onready var set_main_scene_check_box : CheckBox = %SetMainSceneCheckBox +@onready var set_main_scene_button : Button = %SetMainSceneButton +@onready var set_default_theme_check_box : CheckBox = %SetDefaultThemeCheckBox +@onready var set_default_theme_button : Button = %SetDefaultThemeButton +@onready var add_input_icons_check_box : CheckBox = %AddInputIconsCheckBox +@onready var add_input_icons_button : Button = %AddInputIconsButton + +func _refresh_plugin_details() -> void: + for enabled_plugin in ProjectSettings.get_setting("editor_plugins/enabled"): + if enabled_plugin.contains(MaaacksGameTemplatePlugin.get_settings_path()): + var config := ConfigFile.new() + var error = config.load(enabled_plugin) + if error != OK: + return + var current_plugin_version : String = config.get_value("plugin", "version", "0.0.0") + var plugin_name : String = config.get_value("plugin", "name", "Plugin") + plugin_label.text = "%s v%s" % [plugin_name, current_plugin_version] + +func _show_plugin_versions_match() -> void: + update_label.text = "Using Latest Version" + update_check_box.button_pressed = true + update_button.disabled = true + +func _enable_update_plugin_tool_option(tag_name : String) -> void: + update_label.text = "Update to Latest Version v%s" % tag_name + update_button.disabled = false + +func _open_check_plugin_version() -> void: + if check_version_scene_path.is_empty(): + push_warning("Variable \"check_version_scene_path\" is not set") + return + if ProjectSettings.get_setting(MaaacksGameTemplatePlugin.get_settings_path() + "disable_update_check", false): + update_label.text = "Check for Latest Version" + update_button.disabled = false + return + var check_version_scene : PackedScene = load(check_version_scene_path) + var check_version_instance : Node = check_version_scene.instantiate() + check_version_instance.auto_start = true + check_version_instance.new_version_detected.connect(_enable_update_plugin_tool_option) + check_version_instance.versions_matched.connect(_show_plugin_versions_match) + add_child(check_version_instance) + +func _refresh_copy_and_delete_examples() -> void: + var examples_path = MaaacksGameTemplatePlugin.instance.get_plugin_examples_path() + if MaaacksGameTemplatePlugin.get_copy_path() != examples_path: + copy_check_box.button_pressed = true + var dir := DirAccess.open("res://") + if dir.dir_exists(examples_path): + copy_button.disabled = false + delete_button.disabled = false + else: + delete_check_box.button_pressed = true + +func _refresh_main_scene() -> void: + if MaaacksGameTemplatePlugin.instance.is_main_scene_set(): + set_main_scene_check_box.button_pressed = true + else: + set_main_scene_button.disabled = false + +func _refresh_default_theme() -> void: + set_default_theme_button.disabled = false + if ProjectSettings.get_setting("gui/theme/custom", "") != "": + set_default_theme_check_box.button_pressed = true + +func _refresh_input_prompts() -> void: + if input_prompts_directory_path.is_empty(): + push_warning("Variable \"input_prompts_directory_path\" is not set") + return + if DirAccess.dir_exists_absolute(input_prompts_directory_path): + add_input_icons_check_box.button_pressed = true + add_input_icons_button.disabled = false + +func _refresh_options(): + _refresh_plugin_details() + _open_check_plugin_version() + _refresh_copy_and_delete_examples() + _refresh_main_scene() + _refresh_default_theme() + _refresh_input_prompts() + +func _ready(): + _refresh_options() + +func _on_update_button_pressed(): + if ProjectSettings.get_setting(MaaacksGameTemplatePlugin.get_settings_path() + "disable_update_check", false): + ProjectSettings.set_setting(MaaacksGameTemplatePlugin.get_settings_path() + "disable_update_check", false) + _open_check_plugin_version() + return + else: + tree_exited.connect(func(): MaaacksGameTemplatePlugin.instance.open_update_plugin()) + queue_free() + +func _on_copy_button_pressed(): + tree_exited.connect(func(): MaaacksGameTemplatePlugin.instance.open_copy_and_edit_dialog()) + queue_free() + +func _on_delete_button_pressed(): + tree_exited.connect(func(): MaaacksGameTemplatePlugin.instance.open_delete_examples_short_confirmation_dialog()) + queue_free() + +func _on_set_main_scene_button_pressed(): + tree_exited.connect(func(): MaaacksGameTemplatePlugin.instance.open_main_scene_confirmation_dialog(MaaacksGameTemplatePlugin.get_copy_path())) + queue_free() + +func _on_set_default_theme_button_pressed(): + tree_exited.connect(func(): MaaacksGameTemplatePlugin.instance.open_theme_selection_dialog(MaaacksGameTemplatePlugin.get_copy_path())) + queue_free() + +func _on_add_input_icons_button_pressed(): + tree_exited.connect(func(): MaaacksGameTemplatePlugin.instance.open_input_icons_dialog()) + queue_free() diff --git a/addons/maaacks_game_template/installer/setup_wizard.gd.uid b/addons/maaacks_game_template/installer/setup_wizard.gd.uid new file mode 100644 index 0000000..46b8701 --- /dev/null +++ b/addons/maaacks_game_template/installer/setup_wizard.gd.uid @@ -0,0 +1 @@ +uid://dres77mrg8fxf diff --git a/addons/maaacks_game_template/installer/setup_wizard.tscn b/addons/maaacks_game_template/installer/setup_wizard.tscn new file mode 100644 index 0000000..9ff7b47 --- /dev/null +++ b/addons/maaacks_game_template/installer/setup_wizard.tscn @@ -0,0 +1,153 @@ +[gd_scene load_steps=2 format=3 uid="uid://ffy0rjxas5op"] + +[ext_resource type="Script" uid="uid://dres77mrg8fxf" path="res://addons/maaacks_game_template/installer/setup_wizard.gd" id="1_jck5m"] + +[node name="SetupWizardDialog" type="AcceptDialog"] +oversampling_override = 1.0 +title = "Setup Wizard" +initial_position = 2 +size = Vector2i(576, 540) +visible = true +ok_button_text = "Done" +dialog_autowrap = true +script = ExtResource("1_jck5m") +check_version_scene_path = "res://addons/maaacks_game_template/installer/check_plugin_version.tscn" +input_prompts_directory_path = "res://assets/kenney_input-prompts/" + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +custom_minimum_size = Vector2(560, 483) +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 +theme_override_constants/separation = 16 + +[node name="PluginLabel" type="Label" parent="VBoxContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(384, 0) +layout_mode = 2 +autowrap_mode = 3 + +[node name="HSeparator" type="HSeparator" parent="VBoxContainer"] +layout_mode = 2 + +[node name="StepsContainer" type="GridContainer" parent="VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 +theme_override_constants/h_separation = 12 +columns = 3 + +[node name="UpdateLabel" type="Label" parent="VBoxContainer/StepsContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +text = "Update to Latest Version" + +[node name="UpdateCheckBox" type="CheckBox" parent="VBoxContainer/StepsContainer"] +unique_name_in_owner = true +layout_mode = 2 +disabled = true + +[node name="UpdateButton" type="Button" parent="VBoxContainer/StepsContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(100, 0) +layout_mode = 2 +disabled = true +text = "Run" + +[node name="CopyLabel" type="Label" parent="VBoxContainer/StepsContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Copy Example Files" + +[node name="CopyCheckBox" type="CheckBox" parent="VBoxContainer/StepsContainer"] +unique_name_in_owner = true +layout_mode = 2 +disabled = true + +[node name="CopyButton" type="Button" parent="VBoxContainer/StepsContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(100, 0) +layout_mode = 2 +disabled = true +text = "Run" + +[node name="DeleteLabel" type="Label" parent="VBoxContainer/StepsContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Delete Example Files" + +[node name="DeleteCheckBox" type="CheckBox" parent="VBoxContainer/StepsContainer"] +unique_name_in_owner = true +layout_mode = 2 +disabled = true + +[node name="DeleteButton" type="Button" parent="VBoxContainer/StepsContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(100, 0) +layout_mode = 2 +disabled = true +text = "Run" + +[node name="SetMainSceneLabel" type="Label" parent="VBoxContainer/StepsContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Set the Main Scene" + +[node name="SetMainSceneCheckBox" type="CheckBox" parent="VBoxContainer/StepsContainer"] +unique_name_in_owner = true +layout_mode = 2 +disabled = true + +[node name="SetMainSceneButton" type="Button" parent="VBoxContainer/StepsContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(100, 0) +layout_mode = 2 +disabled = true +text = "Run" + +[node name="SetDefaultThemeLabel" type="Label" parent="VBoxContainer/StepsContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Set the Default Theme" + +[node name="SetDefaultThemeCheckBox" type="CheckBox" parent="VBoxContainer/StepsContainer"] +unique_name_in_owner = true +layout_mode = 2 +disabled = true + +[node name="SetDefaultThemeButton" type="Button" parent="VBoxContainer/StepsContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(100, 0) +layout_mode = 2 +disabled = true +text = "Run" + +[node name="AddInputIconsLabel" type="Label" parent="VBoxContainer/StepsContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Add Input Prompt Icons" + +[node name="AddInputIconsCheckBox" type="CheckBox" parent="VBoxContainer/StepsContainer"] +unique_name_in_owner = true +layout_mode = 2 +disabled = true + +[node name="AddInputIconsButton" type="Button" parent="VBoxContainer/StepsContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(100, 0) +layout_mode = 2 +disabled = true +text = "Run" + +[connection signal="pressed" from="VBoxContainer/StepsContainer/UpdateButton" to="." method="_on_update_button_pressed"] +[connection signal="pressed" from="VBoxContainer/StepsContainer/CopyButton" to="." method="_on_copy_button_pressed"] +[connection signal="pressed" from="VBoxContainer/StepsContainer/DeleteButton" to="." method="_on_delete_button_pressed"] +[connection signal="pressed" from="VBoxContainer/StepsContainer/SetMainSceneButton" to="." method="_on_set_main_scene_button_pressed"] +[connection signal="pressed" from="VBoxContainer/StepsContainer/SetDefaultThemeButton" to="." method="_on_set_default_theme_button_pressed"] +[connection signal="pressed" from="VBoxContainer/StepsContainer/AddInputIconsButton" to="." method="_on_add_input_icons_button_pressed"] diff --git a/addons/maaacks_game_template/installer/theme_selection_dialog.gd b/addons/maaacks_game_template/installer/theme_selection_dialog.gd new file mode 100644 index 0000000..db6beb7 --- /dev/null +++ b/addons/maaacks_game_template/installer/theme_selection_dialog.gd @@ -0,0 +1,33 @@ +@tool +extends ConfirmationDialog + +signal theme_selected(theme_file: String) + +@export_dir var theme_directories : Array[String] : + set(value): + theme_directories = value + if is_inside_tree(): + %FileLister.directories = theme_directories + _fill_with_themes() + +func _fill_with_themes() -> void: + %ItemList.clear() + for file in %FileLister.files: + if file is String: + var readable_name = file.get_file().get_basename().capitalize() + %ItemList.add_item(readable_name) + +func _ready() -> void: + get_ok_button().disabled = true + +func _preview_theme(theme_file: String) -> void: + var theme_resource : Theme = load(theme_file) + if theme_resource == null: return + %ThemePreviewContainer.theme = theme_resource + +func _on_item_list_item_selected(index) -> void: + get_ok_button().disabled = false + if index < %FileLister.files.size(): + var file = %FileLister.files[index] + _preview_theme(file) + theme_selected.emit(file) diff --git a/addons/maaacks_game_template/installer/theme_selection_dialog.gd.uid b/addons/maaacks_game_template/installer/theme_selection_dialog.gd.uid new file mode 100644 index 0000000..21322e1 --- /dev/null +++ b/addons/maaacks_game_template/installer/theme_selection_dialog.gd.uid @@ -0,0 +1 @@ +uid://cqlg20n7vu4jx diff --git a/addons/maaacks_game_template/installer/theme_selection_dialog.tscn b/addons/maaacks_game_template/installer/theme_selection_dialog.tscn new file mode 100644 index 0000000..9729365 --- /dev/null +++ b/addons/maaacks_game_template/installer/theme_selection_dialog.tscn @@ -0,0 +1,179 @@ +[gd_scene load_steps=3 format=3 uid="uid://cmqcqq54rj454"] + +[ext_resource type="Script" uid="uid://cqlg20n7vu4jx" path="res://addons/maaacks_game_template/installer/theme_selection_dialog.gd" id="1_5u0gx"] +[ext_resource type="Script" uid="uid://bij7wsh8d44gv" path="res://addons/maaacks_game_template/base/nodes/utilities/file_lister.gd" id="2_luhgx"] + +[node name="ThemeSelectionDialog" type="ConfirmationDialog"] +oversampling_override = 1.0 +title = "Use a Starter Theme" +initial_position = 2 +size = Vector2i(1024, 704) +visible = true +ok_button_text = "Yes" +dialog_autowrap = true +cancel_button_text = "No" +script = ExtResource("1_5u0gx") + +[node name="FileLister" type="Node" parent="."] +unique_name_in_owner = true +script = ExtResource("2_luhgx") +ends_with = ".tres" + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +custom_minimum_size = Vector2(560, 443) +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 +theme_override_constants/separation = 16 + +[node name="Label" type="Label" parent="VBoxContainer"] +custom_minimum_size = Vector2(384, 0) +layout_mode = 2 +text = "Set the projects default theme to a start theme provided below. These can be customized as needed. + +Requires restarting the editor to take full effect." +autowrap_mode = 3 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="ItemList" type="ItemList" parent="VBoxContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="VSeparator" type="VSeparator" parent="VBoxContainer/HBoxContainer"] +layout_mode = 2 + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_constants/margin_left = 16 +theme_override_constants/margin_right = 16 + +[node name="ThemePreviewContainer" type="TabContainer" parent="VBoxContainer/HBoxContainer/MarginContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +current_tab = 0 + +[node name="Tab1" type="Control" parent="VBoxContainer/HBoxContainer/MarginContainer/ThemePreviewContainer"] +layout_mode = 2 +metadata/_tab_index = 0 + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/MarginContainer/ThemePreviewContainer/Tab1"] +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="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/MarginContainer/ThemePreviewContainer/Tab1/MarginContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/MarginContainer/ThemePreviewContainer/Tab1/MarginContainer/VBoxContainer"] +layout_mode = 2 +text = "Label" +horizontal_alignment = 1 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/HBoxContainer/MarginContainer/ThemePreviewContainer/Tab1/MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Button" type="Button" parent="VBoxContainer/HBoxContainer/MarginContainer/ThemePreviewContainer/Tab1/MarginContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Button" + +[node name="Button2" type="Button" parent="VBoxContainer/HBoxContainer/MarginContainer/ThemePreviewContainer/Tab1/MarginContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +toggle_mode = true +button_pressed = true +text = "Button" + +[node name="Button3" type="Button" parent="VBoxContainer/HBoxContainer/MarginContainer/ThemePreviewContainer/Tab1/MarginContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +disabled = true +text = "Button" + +[node name="CheckButton" type="CheckButton" parent="VBoxContainer/HBoxContainer/MarginContainer/ThemePreviewContainer/Tab1/MarginContainer/VBoxContainer"] +layout_mode = 2 +text = "CheckButton" + +[node name="CheckBox" type="CheckBox" parent="VBoxContainer/HBoxContainer/MarginContainer/ThemePreviewContainer/Tab1/MarginContainer/VBoxContainer"] +layout_mode = 2 +text = "CheckBox" + +[node name="MenuButton" type="MenuButton" parent="VBoxContainer/HBoxContainer/MarginContainer/ThemePreviewContainer/Tab1/MarginContainer/VBoxContainer"] +layout_mode = 2 +text = "MenuButton" + +[node name="OptionButton" type="OptionButton" parent="VBoxContainer/HBoxContainer/MarginContainer/ThemePreviewContainer/Tab1/MarginContainer/VBoxContainer"] +layout_mode = 2 +selected = 0 +item_count = 2 +popup/item_0/text = "OptionButton" +popup/item_0/id = 0 +popup/item_1/text = "OptionButton2" +popup/item_1/id = 1 + +[node name="Tab2" type="Control" parent="VBoxContainer/HBoxContainer/MarginContainer/ThemePreviewContainer"] +visible = false +layout_mode = 2 +metadata/_tab_index = 1 + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/MarginContainer/ThemePreviewContainer/Tab2"] +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="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/MarginContainer/ThemePreviewContainer/Tab2/MarginContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/MarginContainer/ThemePreviewContainer/Tab2/MarginContainer/VBoxContainer"] +layout_mode = 2 +text = "Another label" +horizontal_alignment = 1 + +[node name="LineEdit" type="LineEdit" parent="VBoxContainer/HBoxContainer/MarginContainer/ThemePreviewContainer/Tab2/MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="TextEdit" type="TextEdit" parent="VBoxContainer/HBoxContainer/MarginContainer/ThemePreviewContainer/Tab2/MarginContainer/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="HSlider" type="HSlider" parent="VBoxContainer/HBoxContainer/MarginContainer/ThemePreviewContainer/Tab2/MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="HScrollBar" type="HScrollBar" parent="VBoxContainer/HBoxContainer/MarginContainer/ThemePreviewContainer/Tab2/MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="HSeparator" type="HSeparator" parent="VBoxContainer/HBoxContainer/MarginContainer/ThemePreviewContainer/Tab2/MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="ProgressBar" type="ProgressBar" parent="VBoxContainer/HBoxContainer/MarginContainer/ThemePreviewContainer/Tab2/MarginContainer/VBoxContainer"] +layout_mode = 2 +value = 50.0 + +[connection signal="item_selected" from="VBoxContainer/HBoxContainer/ItemList" to="." method="_on_item_list_item_selected"] diff --git a/addons/maaacks_game_template/installer/update_plugin.gd b/addons/maaacks_game_template/installer/update_plugin.gd new file mode 100644 index 0000000..604f223 --- /dev/null +++ b/addons/maaacks_game_template/installer/update_plugin.gd @@ -0,0 +1,174 @@ +@tool +extends Node +## Script for updating the version of a plugin to the latest release on GitHub. + +signal update_completed + +const DownloadAndExtract = MaaacksGameTemplatePlugin.DownloadAndExtract +const APIClient = MaaacksGameTemplatePlugin.APIClient +const ReleaseNotesLabel = preload("./release_notes_label.gd") + +const API_RELEASES_URL := "https://api.github.com/repos/%s/%s/releases" +const UPDATE_CONFIRMATION_MESSAGE := "This will update the contents of the plugin folder (addons/%s/).\nFiles outside of the plugin folder will not be affected.\n\nUpdate %s to v%s?" +const PLUGIN_EXTRACT_PATH := "res://addons/%s/" +const PLUGIN_TEMP_ZIP_PATH := "res://%s_%s_update.zip" + +## The directory of the plugin to update. Typically in res://addons/. +@export var plugin_directory : String +## The URL of the GitHub repo to pull new releases. +@export var plugin_github_url : String : + set(value): + plugin_github_url = value + _update_urls() +@export_group("Advanced") +## If true, automatically download the new version when ready. +@export var auto_start : bool = false +## Text to remove from the tag before showing to the user. +@export var replace_tag_name : String = "v" +## The default lowest version to display. +@export var default_version : String = "0.0.0" +## If true, test getting the new version. +## Replace with @export_tool_button for Godot 4.4+ +@export var _test_action : bool = false : + set(value): + if value and Engine.is_editor_hint(): + get_newest_version() + +@onready var _api_client : APIClient = $APIClient +@onready var _download_and_extract_node : DownloadAndExtract = $DownloadAndExtract +@onready var _update_confirmation_dialog : ConfirmationDialog = $UpdateConfirmationDialog +@onready var _installing_dialog : AcceptDialog = $InstallingDialog +@onready var _error_dialog : AcceptDialog = $ErrorDialog +@onready var _success_dialog : AcceptDialog = $SuccessDialog +@onready var _release_notes_label : ReleaseNotesLabel = %ReleaseNotesLabel +@onready var _update_label : Label = %UpdateLabel +@onready var _warning_message_button : LinkButton = %WarningMessageButton +@onready var _warning_message_label : Label = %WarningMessageLabel +@onready var _release_notes_button : LinkButton = %ReleaseNotesButton +@onready var _release_notes_panel : Panel = %ReleaseNotesPanel +@onready var _stage_label : Label = %StageLabel +@onready var _progress_bar : ProgressBar = %ProgressBar + +var _zipball_url : String +var _newest_version : String +var _plugin_name : String +var _current_plugin_version : String + +func _load_plugin_details() -> void: + if plugin_directory.is_empty(): return + for enabled_plugin in ProjectSettings.get_setting("editor_plugins/enabled"): + if enabled_plugin.contains(plugin_directory): + var config := ConfigFile.new() + var error = config.load(enabled_plugin) + if error != OK: + return + _current_plugin_version = config.get_value("plugin", "version", default_version) + _plugin_name = config.get_value("plugin", "name", "Plugin") + +func _update_urls() -> void: + if plugin_github_url.is_empty(): return + if _api_client == null: return + var regex := RegEx.create_from_string("https:\\/\\/github\\.com\\/([\\w-]+)\\/([\\w-]+)\\/*") + var regex_match := regex.search(plugin_github_url) + if regex_match == null: return + var username := regex_match.get_string(1) + var repository := regex_match.get_string(2) + _api_client.api_url = API_RELEASES_URL % [username, repository] + +func _show_error_dialog(error : String) -> void: + _error_dialog.show() + _error_dialog.dialog_text = "%s!" % error + +func _show_success_dialog() -> void: + _success_dialog.show() + _success_dialog.dialog_text = "%s updated to v%s." % [_plugin_name, _newest_version] + +func _on_api_client_request_failed(error : String) -> void: + _show_error_dialog(error) + +func _on_api_client_response_received(response_body : Variant) -> void: + if response_body is not Array: + push_error("Response was not an array") + return + if response_body.is_empty(): + push_error("Response was an empty array") + return + var latest_release : Dictionary = response_body.front() + _newest_version = default_version + if latest_release.has("tag_name"): + var tag_name = latest_release["tag_name"] + if replace_tag_name: + tag_name = tag_name.replacen(replace_tag_name, "") + _newest_version = tag_name + if latest_release.has("zipball_url"): + _zipball_url = latest_release["zipball_url"] + _download_and_extract_node.zip_url = _zipball_url + _download_and_extract_node.zip_file_path = PLUGIN_TEMP_ZIP_PATH % [plugin_directory, _newest_version] + _update_label.text = UPDATE_CONFIRMATION_MESSAGE % [plugin_directory, _plugin_name, _newest_version] + if latest_release.has("body"): + _release_notes_label.from_release_notes(latest_release["body"]) + _update_confirmation_dialog.show() + +func _on_download_and_extract_zip_saved() -> void: + OS.move_to_trash(ProjectSettings.globalize_path(PLUGIN_EXTRACT_PATH % plugin_directory)) + +func _on_download_and_extract_run_failed(error : String) -> void: + _show_error_dialog(error) + +func _on_download_and_extract_run_completed() -> void: + update_completed.emit() + _show_success_dialog() + +func _on_error_dialog_canceled() -> void: + queue_free() + +func _on_error_dialog_confirmed() -> void: + queue_free() + +func _on_success_dialog_canceled() -> void: + queue_free() + +func _on_success_dialog_confirmed() -> void: + queue_free() + +func _on_update_confirmation_dialog_canceled() -> void: + queue_free() + +func _on_update_confirmation_dialog_confirmed() -> void: + _download_and_extract_node.run() + _installing_dialog.show() + +func _on_warning_message_button_pressed(): + _warning_message_label.show() + _warning_message_button.hide() + +func _on_release_notes_button_pressed() -> void: + _release_notes_panel.show() + _release_notes_button.hide() + +func get_newest_version() -> void: + _api_client.request() + +func _ready() -> void: + _load_plugin_details() + _update_confirmation_dialog.hide() + _installing_dialog.hide() + _error_dialog.hide() + _success_dialog.hide() + if auto_start: + get_newest_version() + +func _process(_delta : float) -> void: + if _installing_dialog.visible: + _progress_bar.value = _download_and_extract_node.get_progress() + match _download_and_extract_node.stage: + DownloadAndExtract.DownloadAndExtractStage.DOWNLOAD: + _stage_label.text = "Downloading..." + DownloadAndExtract.DownloadAndExtractStage.SAVE: + _stage_label.text = "Saving..." + DownloadAndExtract.DownloadAndExtractStage.EXTRACT: + _stage_label.text = "Extracting..." + DownloadAndExtract.DownloadAndExtractStage.DELETE: + _stage_label.text = "Cleaning up..." + DownloadAndExtract.DownloadAndExtractStage.NONE: + _installing_dialog.hide() diff --git a/addons/maaacks_game_template/installer/update_plugin.gd.uid b/addons/maaacks_game_template/installer/update_plugin.gd.uid new file mode 100644 index 0000000..df9e8de --- /dev/null +++ b/addons/maaacks_game_template/installer/update_plugin.gd.uid @@ -0,0 +1 @@ +uid://txkhnw0u8jy4 diff --git a/addons/maaacks_game_template/installer/update_plugin.tscn b/addons/maaacks_game_template/installer/update_plugin.tscn new file mode 100644 index 0000000..8195074 --- /dev/null +++ b/addons/maaacks_game_template/installer/update_plugin.tscn @@ -0,0 +1,158 @@ +[gd_scene load_steps=5 format=3 uid="uid://d28bi7wdv8hbp"] + +[ext_resource type="Script" uid="uid://txkhnw0u8jy4" path="res://addons/maaacks_game_template/installer/update_plugin.gd" id="1_s6qpc"] +[ext_resource type="PackedScene" uid="uid://cf0hkngq1mgfy" path="res://addons/maaacks_game_template/utilities/api_client.tscn" id="2_s6pdq"] +[ext_resource type="PackedScene" uid="uid://5hhnbgqjwnic" path="res://addons/maaacks_game_template/utilities/download_and_extract.tscn" id="3_s6pdq"] +[ext_resource type="Script" uid="uid://cgv1xnmh7vr66" path="res://addons/maaacks_game_template/installer/release_notes_label.gd" id="4_1amwf"] + +[node name="UpdatePlugin" type="Node"] +script = ExtResource("1_s6qpc") +plugin_directory = "maaacks_game_template" +plugin_github_url = "https://github.com/Maaack/Godot-Minimal-Game-Template" + +[node name="APIClient" parent="." instance=ExtResource("2_s6pdq")] +api_url = "https://api.github.com/repos/Maaack/Godot-Minimal-Game-Template/releases" +request_method = 0 + +[node name="DownloadAndExtract" parent="." instance=ExtResource("3_s6pdq")] +extract_path = "res://" +path_match_string = "addons/" +skip_base_zip_dir = true +force = true + +[node name="UpdateConfirmationDialog" type="ConfirmationDialog" parent="."] +auto_translate_mode = 1 +title = "Update Plugin?" +initial_position = 2 +size = Vector2i(640, 360) +dialog_autowrap = true + +[node name="MarginContainer" type="MarginContainer" parent="UpdateConfirmationDialog"] +offset_left = 8.0 +offset_top = 8.0 +offset_right = 632.0 +offset_bottom = 311.0 +theme_override_constants/margin_bottom = 16 + +[node name="VBoxContainer" type="VBoxContainer" parent="UpdateConfirmationDialog/MarginContainer"] +layout_mode = 2 + +[node name="UpdateLabel" type="Label" parent="UpdateConfirmationDialog/MarginContainer/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +text = "This will update the contents of the plugin folder (addons/plugin_directory/). +Files outside of the plugin folder will not be affected. + +Update to v0.0.0?" + +[node name="HSeparator" type="HSeparator" parent="UpdateConfirmationDialog/MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="WarningMessageButton" type="LinkButton" parent="UpdateConfirmationDialog/MarginContainer/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +text = "Warning About Updating" + +[node name="WarningMessageLabel" type="Label" parent="UpdateConfirmationDialog/MarginContainer/VBoxContainer"] +unique_name_in_owner = true +visible = false +custom_minimum_size = Vector2(624, 205) +layout_mode = 2 +text = "Not all updates are backwards compatible! + +Updating can cause issues with previously copied examples. It is recommended to delete any copied example files before starting an update, if it is an option. Otherwise, pay attention to the Release Notes for an idea for how changes may impact your project. + +Lastly, save your work and proceed with caution." +autowrap_mode = 3 + +[node name="ReleaseNotesButton" type="LinkButton" parent="UpdateConfirmationDialog/MarginContainer/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +text = "Show Release Notes" + +[node name="ReleaseNotesPanel" type="Panel" parent="UpdateConfirmationDialog/MarginContainer/VBoxContainer"] +unique_name_in_owner = true +visible = false +custom_minimum_size = Vector2(0, 420) +layout_mode = 2 +size_flags_vertical = 3 + +[node name="ReleaseNotesLabel" type="RichTextLabel" parent="UpdateConfirmationDialog/MarginContainer/VBoxContainer/ReleaseNotesPanel"] +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 +size_flags_vertical = 3 +focus_mode = 2 +bbcode_enabled = true +selection_enabled = true +script = ExtResource("4_1amwf") +h1_font_size = 64 +h2_font_size = 48 +h3_font_size = 32 +h4_font_size = 24 +h5_font_size = 16 +h6_font_size = 12 +bold_headings = true + +[node name="InstallingDialog" type="AcceptDialog" parent="."] +auto_translate_mode = 1 +title = "Installing..." +initial_position = 2 +size = Vector2i(400, 111) + +[node name="MarginContainer" type="MarginContainer" parent="InstallingDialog"] +offset_left = 4.0 +offset_top = 4.0 +offset_right = 396.0 +offset_bottom = 96.0 +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="VBoxContainer" type="VBoxContainer" parent="InstallingDialog/MarginContainer"] +layout_mode = 2 +alignment = 1 + +[node name="StageLabel" type="Label" parent="InstallingDialog/MarginContainer/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="ProgressBar" type="ProgressBar" parent="InstallingDialog/MarginContainer/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +max_value = 1.0 +step = 0.001 + +[node name="ErrorDialog" type="AcceptDialog" parent="."] +auto_translate_mode = 1 +title = "Error!" +initial_position = 2 +size = Vector2i(400, 128) + +[node name="SuccessDialog" type="AcceptDialog" parent="."] +auto_translate_mode = 1 +title = "Update Complete" +initial_position = 2 +size = Vector2i(400, 128) +dialog_text = "%s updated to v%s." + +[connection signal="request_failed" from="APIClient" to="." method="_on_api_client_request_failed"] +[connection signal="response_received" from="APIClient" to="." method="_on_api_client_response_received"] +[connection signal="run_completed" from="DownloadAndExtract" to="." method="_on_download_and_extract_run_completed"] +[connection signal="run_failed" from="DownloadAndExtract" to="." method="_on_download_and_extract_run_failed"] +[connection signal="zip_saved" from="DownloadAndExtract" to="." method="_on_download_and_extract_zip_saved"] +[connection signal="canceled" from="UpdateConfirmationDialog" to="." method="_on_update_confirmation_dialog_canceled"] +[connection signal="confirmed" from="UpdateConfirmationDialog" to="." method="_on_update_confirmation_dialog_confirmed"] +[connection signal="pressed" from="UpdateConfirmationDialog/MarginContainer/VBoxContainer/WarningMessageButton" to="." method="_on_warning_message_button_pressed"] +[connection signal="pressed" from="UpdateConfirmationDialog/MarginContainer/VBoxContainer/ReleaseNotesButton" to="." method="_on_release_notes_button_pressed"] +[connection signal="canceled" from="ErrorDialog" to="." method="_on_error_dialog_canceled"] +[connection signal="confirmed" from="ErrorDialog" to="." method="_on_error_dialog_confirmed"] +[connection signal="canceled" from="SuccessDialog" to="." method="_on_success_dialog_canceled"] +[connection signal="confirmed" from="SuccessDialog" to="." method="_on_success_dialog_confirmed"] diff --git a/addons/maaacks_game_template/media/.gdignore b/addons/maaacks_game_template/media/.gdignore new file mode 100644 index 0000000..e69de29 diff --git a/addons/maaacks_game_template/media/build-and-publish/authorize_butler.png b/addons/maaacks_game_template/media/build-and-publish/authorize_butler.png new file mode 100644 index 0000000..bb26b5f Binary files /dev/null and b/addons/maaacks_game_template/media/build-and-publish/authorize_butler.png differ diff --git a/addons/maaacks_game_template/media/build-and-publish/github-secrets.png b/addons/maaacks_game_template/media/build-and-publish/github-secrets.png new file mode 100644 index 0000000..8e1ef40 Binary files /dev/null and b/addons/maaacks_game_template/media/build-and-publish/github-secrets.png differ diff --git a/addons/maaacks_game_template/media/build-and-publish/github-variables.png b/addons/maaacks_game_template/media/build-and-publish/github-variables.png new file mode 100644 index 0000000..fbfd71a Binary files /dev/null and b/addons/maaacks_game_template/media/build-and-publish/github-variables.png differ diff --git a/addons/maaacks_game_template/media/build-and-publish/godot_export.png b/addons/maaacks_game_template/media/build-and-publish/godot_export.png new file mode 100644 index 0000000..52b112b Binary files /dev/null and b/addons/maaacks_game_template/media/build-and-publish/godot_export.png differ diff --git a/addons/maaacks_game_template/media/build-and-publish/itch_html_missing.png b/addons/maaacks_game_template/media/build-and-publish/itch_html_missing.png new file mode 100644 index 0000000..d693c6d Binary files /dev/null and b/addons/maaacks_game_template/media/build-and-publish/itch_html_missing.png differ diff --git a/addons/maaacks_game_template/media/build-and-publish/itch_html_setting.png b/addons/maaacks_game_template/media/build-and-publish/itch_html_setting.png new file mode 100644 index 0000000..f33c999 Binary files /dev/null and b/addons/maaacks_game_template/media/build-and-publish/itch_html_setting.png differ diff --git a/addons/maaacks_game_template/media/build-and-publish/itch_kind_of_project.png b/addons/maaacks_game_template/media/build-and-publish/itch_kind_of_project.png new file mode 100644 index 0000000..625949f Binary files /dev/null and b/addons/maaacks_game_template/media/build-and-publish/itch_kind_of_project.png differ diff --git a/addons/maaacks_game_template/media/build-and-publish/itch_playable.png b/addons/maaacks_game_template/media/build-and-publish/itch_playable.png new file mode 100644 index 0000000..a015735 Binary files /dev/null and b/addons/maaacks_game_template/media/build-and-publish/itch_playable.png differ diff --git a/addons/maaacks_game_template/media/credits_scene-icon-black-transparent-256x256.png b/addons/maaacks_game_template/media/credits_scene-icon-black-transparent-256x256.png new file mode 100644 index 0000000..42fa46f Binary files /dev/null and b/addons/maaacks_game_template/media/credits_scene-icon-black-transparent-256x256.png differ diff --git a/addons/maaacks_game_template/media/game-icon-black-transparent-256x256.png b/addons/maaacks_game_template/media/game-icon-black-transparent-256x256.png new file mode 100644 index 0000000..ea70a32 Binary files /dev/null and b/addons/maaacks_game_template/media/game-icon-black-transparent-256x256.png differ diff --git a/addons/maaacks_game_template/media/gwj-icon-black-transparent-256x256.png b/addons/maaacks_game_template/media/gwj-icon-black-transparent-256x256.png new file mode 100644 index 0000000..598f426 Binary files /dev/null and b/addons/maaacks_game_template/media/gwj-icon-black-transparent-256x256.png differ diff --git a/addons/maaacks_game_template/media/input_remapping-icon-black-transparent-256x256.png b/addons/maaacks_game_template/media/input_remapping-icon-black-transparent-256x256.png new file mode 100644 index 0000000..6e8dd7d Binary files /dev/null and b/addons/maaacks_game_template/media/input_remapping-icon-black-transparent-256x256.png differ diff --git a/addons/maaacks_game_template/media/maaack-black-transparent-256x256.png b/addons/maaacks_game_template/media/maaack-black-transparent-256x256.png new file mode 100644 index 0000000..23523f6 Binary files /dev/null and b/addons/maaacks_game_template/media/maaack-black-transparent-256x256.png differ diff --git a/addons/maaacks_game_template/media/maaacks-plugin-suite-256x256.gif b/addons/maaacks_game_template/media/maaacks-plugin-suite-256x256.gif new file mode 100644 index 0000000..d5ab8fb Binary files /dev/null and b/addons/maaacks_game_template/media/maaacks-plugin-suite-256x256.gif differ diff --git a/addons/maaacks_game_template/media/menus-icon-black-transparent-256x256.png b/addons/maaacks_game_template/media/menus-icon-black-transparent-256x256.png new file mode 100644 index 0000000..531ffaa Binary files /dev/null and b/addons/maaacks_game_template/media/menus-icon-black-transparent-256x256.png differ diff --git a/addons/maaacks_game_template/media/mini-game-icon-black-transparent-256x256.png b/addons/maaacks_game_template/media/mini-game-icon-black-transparent-256x256.png new file mode 100644 index 0000000..f509071 Binary files /dev/null and b/addons/maaacks_game_template/media/mini-game-icon-black-transparent-256x256.png differ diff --git a/addons/maaacks_game_template/media/music_controller-icon-black-transparent-256x256.png b/addons/maaacks_game_template/media/music_controller-icon-black-transparent-256x256.png new file mode 100644 index 0000000..0e7e1dc Binary files /dev/null and b/addons/maaacks_game_template/media/music_controller-icon-black-transparent-256x256.png differ diff --git a/addons/maaacks_game_template/media/options-icon-black-transparent-256x256.png b/addons/maaacks_game_template/media/options-icon-black-transparent-256x256.png new file mode 100644 index 0000000..ca674b9 Binary files /dev/null and b/addons/maaacks_game_template/media/options-icon-black-transparent-256x256.png differ diff --git a/addons/maaacks_game_template/media/scene_loader-icon-black-transparent-256x256.png b/addons/maaacks_game_template/media/scene_loader-icon-black-transparent-256x256.png new file mode 100644 index 0000000..a40ee1b Binary files /dev/null and b/addons/maaacks_game_template/media/scene_loader-icon-black-transparent-256x256.png differ diff --git a/addons/maaacks_game_template/media/screenshot-5-juliocacko-1.png b/addons/maaacks_game_template/media/screenshot-5-juliocacko-1.png new file mode 100644 index 0000000..f9b609d Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-5-juliocacko-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-5-juliocacko-2.png b/addons/maaacks_game_template/media/screenshot-5-juliocacko-2.png new file mode 100644 index 0000000..51da311 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-5-juliocacko-2.png differ diff --git a/addons/maaacks_game_template/media/screenshot-5-kenney-1.png b/addons/maaacks_game_template/media/screenshot-5-kenney-1.png new file mode 100644 index 0000000..8286e7f Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-5-kenney-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-5-kenney-2.png b/addons/maaacks_game_template/media/screenshot-5-kenney-2.png new file mode 100644 index 0000000..ba8455c Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-5-kenney-2.png differ diff --git a/addons/maaacks_game_template/media/screenshot-5-kenney-3.png b/addons/maaacks_game_template/media/screenshot-5-kenney-3.png new file mode 100644 index 0000000..ad1eebc Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-5-kenney-3.png differ diff --git a/addons/maaacks_game_template/media/screenshot-5-kenney-4.png b/addons/maaacks_game_template/media/screenshot-5-kenney-4.png new file mode 100644 index 0000000..19c2343 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-5-kenney-4.png differ diff --git a/addons/maaacks_game_template/media/screenshot-5-xelu-1.png b/addons/maaacks_game_template/media/screenshot-5-xelu-1.png new file mode 100644 index 0000000..157614c Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-5-xelu-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-5-xelu-2.png b/addons/maaacks_game_template/media/screenshot-5-xelu-2.png new file mode 100644 index 0000000..acb0964 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-5-xelu-2.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-audio-options-1.png b/addons/maaacks_game_template/media/screenshot-6-audio-options-1.png new file mode 100644 index 0000000..d5d504c Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-audio-options-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-audio-options-2.png b/addons/maaacks_game_template/media/screenshot-6-audio-options-2.png new file mode 100644 index 0000000..0020cd7 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-audio-options-2.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-audio-options-3.png b/addons/maaacks_game_template/media/screenshot-6-audio-options-3.png new file mode 100644 index 0000000..cb10028 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-audio-options-3.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-audio-options-4.png b/addons/maaacks_game_template/media/screenshot-6-audio-options-4.png new file mode 100644 index 0000000..02ccf47 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-audio-options-4.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-audio-options-5.png b/addons/maaacks_game_template/media/screenshot-6-audio-options-5.png new file mode 100644 index 0000000..afae5fa Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-audio-options-5.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-audio-options-6.png b/addons/maaacks_game_template/media/screenshot-6-audio-options-6.png new file mode 100644 index 0000000..a2a0161 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-audio-options-6.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-input-list-1.png b/addons/maaacks_game_template/media/screenshot-6-input-list-1.png new file mode 100644 index 0000000..13b9478 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-input-list-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-input-list-2.png b/addons/maaacks_game_template/media/screenshot-6-input-list-2.png new file mode 100644 index 0000000..c9c6936 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-input-list-2.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-input-list-3.png b/addons/maaacks_game_template/media/screenshot-6-input-list-3.png new file mode 100644 index 0000000..d1652b0 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-input-list-3.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-input-list-4.png b/addons/maaacks_game_template/media/screenshot-6-input-list-4.png new file mode 100644 index 0000000..8e649cd Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-input-list-4.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-input-list-5.png b/addons/maaacks_game_template/media/screenshot-6-input-list-5.png new file mode 100644 index 0000000..f882582 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-input-list-5.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-input-list-6.png b/addons/maaacks_game_template/media/screenshot-6-input-list-6.png new file mode 100644 index 0000000..89d9cad Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-input-list-6.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-input-list-7.png b/addons/maaacks_game_template/media/screenshot-6-input-list-7.png new file mode 100644 index 0000000..37dc102 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-input-list-7.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-input-list-8.png b/addons/maaacks_game_template/media/screenshot-6-input-list-8.png new file mode 100644 index 0000000..35253e0 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-input-list-8.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-input-list-9.png b/addons/maaacks_game_template/media/screenshot-6-input-list-9.png new file mode 100644 index 0000000..c863208 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-input-list-9.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-input-sensitivity-1.png b/addons/maaacks_game_template/media/screenshot-6-input-sensitivity-1.png new file mode 100644 index 0000000..e308813 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-input-sensitivity-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-input-sensitivity-2.png b/addons/maaacks_game_template/media/screenshot-6-input-sensitivity-2.png new file mode 100644 index 0000000..6236ced Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-input-sensitivity-2.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-input-sensitivity-3.png b/addons/maaacks_game_template/media/screenshot-6-input-sensitivity-3.png new file mode 100644 index 0000000..6e42860 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-input-sensitivity-3.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-input-sensitivity-4.png b/addons/maaacks_game_template/media/screenshot-6-input-sensitivity-4.png new file mode 100644 index 0000000..e4f1377 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-input-sensitivity-4.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-input-sensitivity-5.png b/addons/maaacks_game_template/media/screenshot-6-input-sensitivity-5.png new file mode 100644 index 0000000..9a6c9cb Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-input-sensitivity-5.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-input-tree-1.png b/addons/maaacks_game_template/media/screenshot-6-input-tree-1.png new file mode 100644 index 0000000..cb187d8 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-input-tree-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-input-tree-2.png b/addons/maaacks_game_template/media/screenshot-6-input-tree-2.png new file mode 100644 index 0000000..32e03ff Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-input-tree-2.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-input-tree-3.png b/addons/maaacks_game_template/media/screenshot-6-input-tree-3.png new file mode 100644 index 0000000..b7c0305 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-input-tree-3.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-input-tree-4.png b/addons/maaacks_game_template/media/screenshot-6-input-tree-4.png new file mode 100644 index 0000000..062d982 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-input-tree-4.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-input-tree-5.png b/addons/maaacks_game_template/media/screenshot-6-input-tree-5.png new file mode 100644 index 0000000..af86c6c Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-input-tree-5.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-level-lost-1.png b/addons/maaacks_game_template/media/screenshot-6-level-lost-1.png new file mode 100644 index 0000000..eb1e243 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-level-lost-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-level-lost-2.png b/addons/maaacks_game_template/media/screenshot-6-level-lost-2.png new file mode 100644 index 0000000..5c8ecdb Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-level-lost-2.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-level-lost-3.png b/addons/maaacks_game_template/media/screenshot-6-level-lost-3.png new file mode 100644 index 0000000..61b3583 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-level-lost-3.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-level-select-1.png b/addons/maaacks_game_template/media/screenshot-6-level-select-1.png new file mode 100644 index 0000000..1bc5dd8 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-level-select-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-level-state-1.png b/addons/maaacks_game_template/media/screenshot-6-level-state-1.png new file mode 100644 index 0000000..69e11a1 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-level-state-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-level-state-2.png b/addons/maaacks_game_template/media/screenshot-6-level-state-2.png new file mode 100644 index 0000000..69fab0b Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-level-state-2.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-level-state-3.png b/addons/maaacks_game_template/media/screenshot-6-level-state-3.png new file mode 100644 index 0000000..f90392c Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-level-state-3.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-level-won-1.png b/addons/maaacks_game_template/media/screenshot-6-level-won-1.png new file mode 100644 index 0000000..b1ce24f Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-level-won-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-level-won-2.png b/addons/maaacks_game_template/media/screenshot-6-level-won-2.png new file mode 100644 index 0000000..d9042d1 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-level-won-2.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-level-won-3.png b/addons/maaacks_game_template/media/screenshot-6-level-won-3.png new file mode 100644 index 0000000..d0a02f0 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-level-won-3.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-loading-screen-1.png b/addons/maaacks_game_template/media/screenshot-6-loading-screen-1.png new file mode 100644 index 0000000..789ed03 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-loading-screen-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-loading-screen-2.png b/addons/maaacks_game_template/media/screenshot-6-loading-screen-2.png new file mode 100644 index 0000000..7aefce7 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-loading-screen-2.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-loading-screen-3.png b/addons/maaacks_game_template/media/screenshot-6-loading-screen-3.png new file mode 100644 index 0000000..ecaa3ed Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-loading-screen-3.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-loading-screen-4.png b/addons/maaacks_game_template/media/screenshot-6-loading-screen-4.png new file mode 100644 index 0000000..56e40c0 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-loading-screen-4.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-main-menu-1.png b/addons/maaacks_game_template/media/screenshot-6-main-menu-1.png new file mode 100644 index 0000000..ebbfeff Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-main-menu-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-main-menu-2.png b/addons/maaacks_game_template/media/screenshot-6-main-menu-2.png new file mode 100644 index 0000000..6aa9637 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-main-menu-2.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-main-menu-3.png b/addons/maaacks_game_template/media/screenshot-6-main-menu-3.png new file mode 100644 index 0000000..b7f6caa Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-main-menu-3.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-main-menu-4.png b/addons/maaacks_game_template/media/screenshot-6-main-menu-4.png new file mode 100644 index 0000000..e5285ab Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-main-menu-4.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-main-menu-5.png b/addons/maaacks_game_template/media/screenshot-6-main-menu-5.png new file mode 100644 index 0000000..871aeb7 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-main-menu-5.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-mini-options-1.png b/addons/maaacks_game_template/media/screenshot-6-mini-options-1.png new file mode 100644 index 0000000..57e1e58 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-mini-options-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-mini-options-2.png b/addons/maaacks_game_template/media/screenshot-6-mini-options-2.png new file mode 100644 index 0000000..22c5133 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-mini-options-2.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-pause-menu-1.png b/addons/maaacks_game_template/media/screenshot-6-pause-menu-1.png new file mode 100644 index 0000000..2e2a30b Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-pause-menu-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-pause-menu-2.png b/addons/maaacks_game_template/media/screenshot-6-pause-menu-2.png new file mode 100644 index 0000000..5e30fa2 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-pause-menu-2.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-pause-menu-3.png b/addons/maaacks_game_template/media/screenshot-6-pause-menu-3.png new file mode 100644 index 0000000..795eac5 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-pause-menu-3.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-pause-menu-4.png b/addons/maaacks_game_template/media/screenshot-6-pause-menu-4.png new file mode 100644 index 0000000..c248cf1 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-pause-menu-4.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-pause-menu-5.png b/addons/maaacks_game_template/media/screenshot-6-pause-menu-5.png new file mode 100644 index 0000000..5cb7c36 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-pause-menu-5.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-theme-selector-1.png b/addons/maaacks_game_template/media/screenshot-6-theme-selector-1.png new file mode 100644 index 0000000..cac8c8e Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-theme-selector-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-theme-selector-10.png b/addons/maaacks_game_template/media/screenshot-6-theme-selector-10.png new file mode 100644 index 0000000..989ebcf Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-theme-selector-10.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-theme-selector-11.png b/addons/maaacks_game_template/media/screenshot-6-theme-selector-11.png new file mode 100644 index 0000000..c970f2e Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-theme-selector-11.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-theme-selector-12.png b/addons/maaacks_game_template/media/screenshot-6-theme-selector-12.png new file mode 100644 index 0000000..50d2728 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-theme-selector-12.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-theme-selector-2.png b/addons/maaacks_game_template/media/screenshot-6-theme-selector-2.png new file mode 100644 index 0000000..ccbcf1a Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-theme-selector-2.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-theme-selector-3.png b/addons/maaacks_game_template/media/screenshot-6-theme-selector-3.png new file mode 100644 index 0000000..6c696fa Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-theme-selector-3.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-theme-selector-4.png b/addons/maaacks_game_template/media/screenshot-6-theme-selector-4.png new file mode 100644 index 0000000..63bc601 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-theme-selector-4.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-theme-selector-5.png b/addons/maaacks_game_template/media/screenshot-6-theme-selector-5.png new file mode 100644 index 0000000..a5f85a3 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-theme-selector-5.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-theme-selector-6.png b/addons/maaacks_game_template/media/screenshot-6-theme-selector-6.png new file mode 100644 index 0000000..b9367a8 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-theme-selector-6.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-theme-selector-7.png b/addons/maaacks_game_template/media/screenshot-6-theme-selector-7.png new file mode 100644 index 0000000..3108e07 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-theme-selector-7.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-theme-selector-8.png b/addons/maaacks_game_template/media/screenshot-6-theme-selector-8.png new file mode 100644 index 0000000..2f3838a Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-theme-selector-8.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-theme-selector-9.png b/addons/maaacks_game_template/media/screenshot-6-theme-selector-9.png new file mode 100644 index 0000000..ebdb9be Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-theme-selector-9.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-video-options-1.png b/addons/maaacks_game_template/media/screenshot-6-video-options-1.png new file mode 100644 index 0000000..6cc7c52 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-video-options-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-video-options-2.png b/addons/maaacks_game_template/media/screenshot-6-video-options-2.png new file mode 100644 index 0000000..efbdd68 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-video-options-2.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-video-options-3.png b/addons/maaacks_game_template/media/screenshot-6-video-options-3.png new file mode 100644 index 0000000..928a2ee Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-video-options-3.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-video-options-4.png b/addons/maaacks_game_template/media/screenshot-6-video-options-4.png new file mode 100644 index 0000000..16ff796 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-video-options-4.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-video-options-5.png b/addons/maaacks_game_template/media/screenshot-6-video-options-5.png new file mode 100644 index 0000000..f11b1e1 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-video-options-5.png differ diff --git a/addons/maaacks_game_template/media/screenshot-6-video-options-6.png b/addons/maaacks_game_template/media/screenshot-6-video-options-6.png new file mode 100644 index 0000000..54a5228 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-6-video-options-6.png differ diff --git a/addons/maaacks_game_template/media/screenshot-7-audio-options-1.png b/addons/maaacks_game_template/media/screenshot-7-audio-options-1.png new file mode 100644 index 0000000..e1101bd Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-7-audio-options-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-7-credits-1.png b/addons/maaacks_game_template/media/screenshot-7-credits-1.png new file mode 100644 index 0000000..71ae041 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-7-credits-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-7-credits-2.png b/addons/maaacks_game_template/media/screenshot-7-credits-2.png new file mode 100644 index 0000000..1c8d283 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-7-credits-2.png differ diff --git a/addons/maaacks_game_template/media/screenshot-7-end-credits-1.png b/addons/maaacks_game_template/media/screenshot-7-end-credits-1.png new file mode 100644 index 0000000..2df47c5 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-7-end-credits-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-7-end-credits-2.png b/addons/maaacks_game_template/media/screenshot-7-end-credits-2.png new file mode 100644 index 0000000..77fd085 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-7-end-credits-2.png differ diff --git a/addons/maaacks_game_template/media/screenshot-7-end-credits-3.png b/addons/maaacks_game_template/media/screenshot-7-end-credits-3.png new file mode 100644 index 0000000..36916d7 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-7-end-credits-3.png differ diff --git a/addons/maaacks_game_template/media/screenshot-7-end-credits-4.png b/addons/maaacks_game_template/media/screenshot-7-end-credits-4.png new file mode 100644 index 0000000..263973e Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-7-end-credits-4.png differ diff --git a/addons/maaacks_game_template/media/screenshot-7-game-won-1.png b/addons/maaacks_game_template/media/screenshot-7-game-won-1.png new file mode 100644 index 0000000..3a5be94 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-7-game-won-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-7-input-options-1.png b/addons/maaacks_game_template/media/screenshot-7-input-options-1.png new file mode 100644 index 0000000..a19f930 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-7-input-options-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-7-input-remapping-1.png b/addons/maaacks_game_template/media/screenshot-7-input-remapping-1.png new file mode 100644 index 0000000..bb3024e Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-7-input-remapping-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-7-input-remapping-2.png b/addons/maaacks_game_template/media/screenshot-7-input-remapping-2.png new file mode 100644 index 0000000..360ce3d Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-7-input-remapping-2.png differ diff --git a/addons/maaacks_game_template/media/screenshot-7-input-remapping-3.png b/addons/maaacks_game_template/media/screenshot-7-input-remapping-3.png new file mode 100644 index 0000000..8fcfa2a Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-7-input-remapping-3.png differ diff --git a/addons/maaacks_game_template/media/screenshot-7-input-remapping-4.png b/addons/maaacks_game_template/media/screenshot-7-input-remapping-4.png new file mode 100644 index 0000000..fa6b50a Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-7-input-remapping-4.png differ diff --git a/addons/maaacks_game_template/media/screenshot-7-input-remapping-5.png b/addons/maaacks_game_template/media/screenshot-7-input-remapping-5.png new file mode 100644 index 0000000..24a8dbb Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-7-input-remapping-5.png differ diff --git a/addons/maaacks_game_template/media/screenshot-7-level-1.png b/addons/maaacks_game_template/media/screenshot-7-level-1.png new file mode 100644 index 0000000..02f8bc0 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-7-level-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-7-level-2.png b/addons/maaacks_game_template/media/screenshot-7-level-2.png new file mode 100644 index 0000000..9243e20 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-7-level-2.png differ diff --git a/addons/maaacks_game_template/media/screenshot-7-level-lost-1.png b/addons/maaacks_game_template/media/screenshot-7-level-lost-1.png new file mode 100644 index 0000000..2caca8d Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-7-level-lost-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-7-level-won-1.png b/addons/maaacks_game_template/media/screenshot-7-level-won-1.png new file mode 100644 index 0000000..3bef366 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-7-level-won-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-7-loading-screen-1.png b/addons/maaacks_game_template/media/screenshot-7-loading-screen-1.png new file mode 100644 index 0000000..83d2ce4 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-7-loading-screen-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-7-loading-screen-2.png b/addons/maaacks_game_template/media/screenshot-7-loading-screen-2.png new file mode 100644 index 0000000..3f26c4e Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-7-loading-screen-2.png differ diff --git a/addons/maaacks_game_template/media/screenshot-7-loading-screen-3.png b/addons/maaacks_game_template/media/screenshot-7-loading-screen-3.png new file mode 100644 index 0000000..a4a4221 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-7-loading-screen-3.png differ diff --git a/addons/maaacks_game_template/media/screenshot-7-loading-screen-4.png b/addons/maaacks_game_template/media/screenshot-7-loading-screen-4.png new file mode 100644 index 0000000..37714f5 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-7-loading-screen-4.png differ diff --git a/addons/maaacks_game_template/media/screenshot-7-loading-screen-5.png b/addons/maaacks_game_template/media/screenshot-7-loading-screen-5.png new file mode 100644 index 0000000..b477e8e Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-7-loading-screen-5.png differ diff --git a/addons/maaacks_game_template/media/screenshot-7-loading-screen-6.png b/addons/maaacks_game_template/media/screenshot-7-loading-screen-6.png new file mode 100644 index 0000000..3717561 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-7-loading-screen-6.png differ diff --git a/addons/maaacks_game_template/media/screenshot-7-main-menu-1.png b/addons/maaacks_game_template/media/screenshot-7-main-menu-1.png new file mode 100644 index 0000000..7edf067 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-7-main-menu-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-7-tutorial-1.png b/addons/maaacks_game_template/media/screenshot-7-tutorial-1.png new file mode 100644 index 0000000..c4963d6 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-7-tutorial-1.png differ diff --git a/addons/maaacks_game_template/media/screenshot-7-video-options-1.png b/addons/maaacks_game_template/media/screenshot-7-video-options-1.png new file mode 100644 index 0000000..65fe081 Binary files /dev/null and b/addons/maaacks_game_template/media/screenshot-7-video-options-1.png differ diff --git a/addons/maaacks_game_template/media/thumbnail-game-a-darkness-like-gravity.png b/addons/maaacks_game_template/media/thumbnail-game-a-darkness-like-gravity.png new file mode 100644 index 0000000..880e5cb Binary files /dev/null and b/addons/maaacks_game_template/media/thumbnail-game-a-darkness-like-gravity.png differ diff --git a/addons/maaacks_game_template/media/thumbnail-game-baking-godium.png b/addons/maaacks_game_template/media/thumbnail-game-baking-godium.png new file mode 100644 index 0000000..a8788ae Binary files /dev/null and b/addons/maaacks_game_template/media/thumbnail-game-baking-godium.png differ diff --git a/addons/maaacks_game_template/media/thumbnail-game-rent-seek-kill.png b/addons/maaacks_game_template/media/thumbnail-game-rent-seek-kill.png new file mode 100644 index 0000000..9b67b6d Binary files /dev/null and b/addons/maaacks_game_template/media/thumbnail-game-rent-seek-kill.png differ diff --git a/addons/maaacks_game_template/media/thumbnail-game-spud-customs.png b/addons/maaacks_game_template/media/thumbnail-game-spud-customs.png new file mode 100644 index 0000000..59a2d7e Binary files /dev/null and b/addons/maaacks_game_template/media/thumbnail-game-spud-customs.png differ diff --git a/addons/maaacks_game_template/media/ui_sound_controller-icon-black-transparent-256x256.png b/addons/maaacks_game_template/media/ui_sound_controller-icon-black-transparent-256x256.png new file mode 100644 index 0000000..30cbe58 Binary files /dev/null and b/addons/maaacks_game_template/media/ui_sound_controller-icon-black-transparent-256x256.png differ diff --git a/addons/maaacks_game_template/plugin.cfg b/addons/maaacks_game_template/plugin.cfg new file mode 100644 index 0000000..dad8f8d --- /dev/null +++ b/addons/maaacks_game_template/plugin.cfg @@ -0,0 +1,9 @@ +[plugin] + +name="Maaack's Minimal Game Template" +description="Template with a main menu, options menus, pause menu, credits, scene loader, extra tools, and an example game scene. + +Created in collaboration with members of the Godot Wild Jam community." +author="Marek Belski" +version="1.4.3" +script="plugin.gd" diff --git a/addons/maaacks_game_template/plugin.gd b/addons/maaacks_game_template/plugin.gd new file mode 100644 index 0000000..2decc96 --- /dev/null +++ b/addons/maaacks_game_template/plugin.gd @@ -0,0 +1,320 @@ +@tool +class_name MaaacksGameTemplatePlugin +extends EditorPlugin + +const PLUGIN_PATH = "res://addons/maaacks_game_template/" +const PLUGIN_NAME = "Maaack's Minimal Game Template" +const PROJECT_SETTINGS_PATH = "maaacks_game_template/" + +const APIClient = preload(PLUGIN_PATH + "utilities/api_client.gd") +const DownloadAndExtract = preload(PLUGIN_PATH + "utilities/download_and_extract.gd") +const CopyAndEdit = preload(PLUGIN_PATH + "installer/copy_and_edit_files.gd") + +const EXAMPLES_RELATIVE_PATH = "examples/" +const MAIN_SCENE_RELATIVE_PATH = "scenes/opening/opening.tscn" +const OVERRIDE_RELATIVE_PATH = "installer/override.cfg" +const THEMES_DIRECTORY_RELATIVE_PATH = "resources/themes" +const WINDOW_OPEN_DELAY : float = 0.5 +const RUNNING_CHECK_DELAY : float = 0.25 +const OPEN_EDITOR_DELAY : float = 0.1 +const MAX_PHYSICS_FRAMES_FROM_START : int = 60 +const AVAILABLE_TRANSLATIONS : Array = ["en", "fr"] + +static var instance : MaaacksGameTemplatePlugin + +var selected_theme : String +var update_plugin_tool_string : String + +static func get_plugin_name() -> String: + return PLUGIN_NAME + +static func get_settings_path() -> String: + return PROJECT_SETTINGS_PATH + +static func get_plugin_path() -> String: + return PLUGIN_PATH + +static func get_plugin_examples_path() -> String: + return get_plugin_path() + EXAMPLES_RELATIVE_PATH + +static func get_copy_path() -> String: + var copy_path = ProjectSettings.get_setting(PROJECT_SETTINGS_PATH + "copy_path", get_plugin_examples_path()) + if not copy_path.ends_with("/"): + copy_path += "/" + return copy_path + +func _on_theme_selected(theme_resource_path: String) -> void: + selected_theme = theme_resource_path + +func _update_gui_theme() -> void: + if selected_theme.is_empty(): return + ProjectSettings.set_setting("gui/theme/custom", selected_theme) + ProjectSettings.save() + +func _on_visibility_changed_to_hidden(dialog_window : Window) -> void: + if dialog_window and dialog_window.is_inside_tree() and not dialog_window.visible: + dialog_window.queue_free() + +func open_theme_selection_dialog(target_path : String) -> void: + selected_theme = "" + var theme_selection_scene : PackedScene = load(get_plugin_path() + "installer/theme_selection_dialog.tscn") + var theme_selection_instance : ConfirmationDialog = theme_selection_scene.instantiate() + theme_selection_instance.confirmed.connect(_update_gui_theme) + theme_selection_instance.theme_selected.connect(_on_theme_selected) + theme_selection_instance.visibility_changed.connect(_on_visibility_changed_to_hidden.bind(theme_selection_instance)) + add_child(theme_selection_instance) + var theme_directores : Array[String] + theme_directores.append(target_path + THEMES_DIRECTORY_RELATIVE_PATH) + theme_selection_instance.theme_directories = theme_directores + +func open_setup_complete_dialog(_target_path : String) -> void: + var setup_complete_scene : PackedScene = load(get_plugin_path() + "installer/setup_complete_dialog.tscn") + var setup_complete_instance : AcceptDialog = setup_complete_scene.instantiate() + setup_complete_instance.visibility_changed.connect(_on_visibility_changed_to_hidden.bind(setup_complete_instance)) + add_child(setup_complete_instance) + +func _delayed_open_setup_complete_dialog(target_path : String) -> void: + var timer: Timer = Timer.new() + var callable := func(): + timer.stop() + open_setup_complete_dialog(target_path) + timer.queue_free() + timer.timeout.connect(callable) + add_child(timer) + timer.start(WINDOW_OPEN_DELAY) + +func _update_main_scene(target_path : String, main_scene_path : String) -> void: + ProjectSettings.set_setting("application/run/main_scene", main_scene_path) + ProjectSettings.save() + _delayed_open_setup_complete_dialog(target_path) + +func is_main_scene_set(target_path : String = get_copy_path()) -> bool: + var current_main_scene_path = ProjectSettings.get_setting("application/run/main_scene", "") + var new_main_scene_path = target_path + MAIN_SCENE_RELATIVE_PATH + return current_main_scene_path == new_main_scene_path + +func _check_main_scene_needs_updating(target_path : String) -> void: + if not is_main_scene_set(target_path): + open_main_scene_confirmation_dialog(target_path) + return + _delayed_open_setup_complete_dialog(target_path) + +func open_main_scene_confirmation_dialog(target_path : String) -> void: + var main_confirmation_scene : PackedScene = load(get_plugin_path() + "installer/main_scene_confirmation_dialog.tscn") + var main_confirmation_instance : ConfirmationDialog = main_confirmation_scene.instantiate() + var new_main_scene_path = target_path + MAIN_SCENE_RELATIVE_PATH + if main_confirmation_instance.has_method(&"set_main_scene_text"): + main_confirmation_instance.set_main_scene_text(new_main_scene_path) + main_confirmation_instance.confirmed.connect(_update_main_scene.bind(target_path, new_main_scene_path)) + main_confirmation_instance.canceled.connect(_delayed_open_setup_complete_dialog.bind(target_path)) + main_confirmation_instance.visibility_changed.connect(_on_visibility_changed_to_hidden.bind(main_confirmation_instance)) + add_child(main_confirmation_instance) + +func _open_play_opening_confirmation_dialog(target_path : String) -> void: + var play_confirmation_scene : PackedScene = load(get_plugin_path() + "installer/play_opening_confirmation_dialog.tscn") + var play_confirmation_instance : ConfirmationDialog = play_confirmation_scene.instantiate() + play_confirmation_instance.confirmed.connect(_run_opening_scene.bind(target_path)) + play_confirmation_instance.canceled.connect(_check_main_scene_needs_updating.bind(target_path)) + play_confirmation_instance.visibility_changed.connect(_on_visibility_changed_to_hidden.bind(play_confirmation_instance)) + add_child(play_confirmation_instance) + +func _open_delete_examples_confirmation_dialog(target_path : String) -> void: + var delete_confirmation_scene : PackedScene = load(get_plugin_path() + "installer/delete_examples_confirmation_dialog.tscn") + var delete_confirmation_instance : ConfirmationDialog = delete_confirmation_scene.instantiate() + delete_confirmation_instance.confirmed.connect(_delete_source_examples_directory.bind(target_path)) + delete_confirmation_instance.canceled.connect(_check_main_scene_needs_updating.bind(target_path)) + delete_confirmation_instance.visibility_changed.connect(_on_visibility_changed_to_hidden.bind(delete_confirmation_instance)) + add_child(delete_confirmation_instance) + +func open_delete_examples_short_confirmation_dialog() -> void: + var delete_confirmation_scene : PackedScene = load(get_plugin_path() + "installer/delete_examples_short_confirmation_dialog.tscn") + var delete_confirmation_instance : ConfirmationDialog = delete_confirmation_scene.instantiate() + delete_confirmation_instance.confirmed.connect(_delete_source_examples_directory) + delete_confirmation_instance.visibility_changed.connect(_on_visibility_changed_to_hidden.bind(delete_confirmation_instance)) + add_child(delete_confirmation_instance) + +func _run_opening_scene(target_path : String) -> void: + var opening_scene_path = target_path + MAIN_SCENE_RELATIVE_PATH + EditorInterface.play_custom_scene(opening_scene_path) + var timer: Timer = Timer.new() + var callable := func() -> void: + if EditorInterface.is_playing_scene(): return + timer.stop() + _open_delete_examples_confirmation_dialog(target_path) + timer.queue_free() + timer.timeout.connect(callable) + add_child(timer) + timer.start(RUNNING_CHECK_DELAY) + +func _delete_directory_recursive(dir_path : String) -> void: + if not dir_path.ends_with("/"): + dir_path += "/" + var dir = DirAccess.open(dir_path) + if dir: + dir.list_dir_begin() + var file_name = dir.get_next() + var error : Error + while file_name != "" and error == 0: + var relative_path = dir_path.trim_prefix(get_plugin_examples_path()) + var full_file_path = dir_path + file_name + if dir.current_is_dir(): + _delete_directory_recursive(full_file_path) + else: + error = dir.remove(file_name) + file_name = dir.get_next() + if error: + push_error("plugin error - deleting path: %s" % error) + else: + push_error("plugin error - accessing path: %s" % dir) + dir.remove(dir_path) + +func _delete_source_examples_directory(target_path : String = "") -> void: + var examples_path = get_plugin_examples_path() + var dir := DirAccess.open("res://") + if dir.dir_exists(examples_path): + _delete_directory_recursive(examples_path) + EditorInterface.get_resource_filesystem().scan() + if not target_path.is_empty(): + _check_main_scene_needs_updating(target_path) + +func _raw_copy_file_path(file_path : String, destination_path : String) -> Error: + var dir := DirAccess.open("res://") + var error := dir.copy(file_path, destination_path) + return error + +func _copy_override_file() -> void: + var override_path : String = get_plugin_path() + OVERRIDE_RELATIVE_PATH + _raw_copy_file_path(override_path, "res://"+override_path.get_file()) + +func _add_translations() -> void: + var dir := DirAccess.open("res://") + var translations : PackedStringArray = ProjectSettings.get_setting("internationalization/locale/translations", []) + for available_translation in AVAILABLE_TRANSLATIONS: + var translation_path = get_plugin_path() + ("base/translations/menus_translations.%s.translation" % available_translation) + if dir.file_exists(translation_path) and translation_path not in translations: + translations.append(translation_path) + ProjectSettings.set_setting("internationalization/locale/translations", translations) + +func _on_completed_copy_to_directory(target_path : String) -> void: + ProjectSettings.set_setting(PROJECT_SETTINGS_PATH + "copy_path", target_path) + ProjectSettings.save() + _copy_override_file() + _open_play_opening_confirmation_dialog(target_path) + +func open_input_icons_dialog() -> void: + var input_icons_scene : PackedScene = load(get_plugin_path() + "installer/kenney_input_prompts_installer.tscn") + var input_icons_instance = input_icons_scene.instantiate() + input_icons_instance.copy_dir_path = get_copy_path() + add_child(input_icons_instance) + +func open_copy_and_edit_dialog() -> void: + var copy_and_edit_scene : PackedScene = load(get_plugin_path() + "installer/copy_and_edit_files.tscn") + var copy_and_edit_instance : CopyAndEdit = copy_and_edit_scene.instantiate() + copy_and_edit_instance.completed.connect(_on_completed_copy_to_directory) + copy_and_edit_instance.canceled.connect(_check_main_scene_needs_updating.bind(get_copy_path())) + add_child(copy_and_edit_instance) + +func _open_confirmation_dialog() -> void: + var confirmation_scene : PackedScene = load(get_plugin_path() + "installer/copy_confirmation_dialog.tscn") + var confirmation_instance : ConfirmationDialog = confirmation_scene.instantiate() + confirmation_instance.confirmed.connect(open_copy_and_edit_dialog) + confirmation_instance.canceled.connect(_check_main_scene_needs_updating.bind(get_copy_path())) + confirmation_instance.visibility_changed.connect(_on_visibility_changed_to_hidden.bind(confirmation_instance)) + add_child(confirmation_instance) + +func _open_check_plugin_version() -> void: + if ProjectSettings.has_setting(PROJECT_SETTINGS_PATH + "disable_update_check"): + if ProjectSettings.get_setting(PROJECT_SETTINGS_PATH + "disable_update_check"): + return + else: + ProjectSettings.set_setting(PROJECT_SETTINGS_PATH + "disable_update_check", false) + ProjectSettings.save() + var check_version_scene : PackedScene = load(get_plugin_path() + "installer/check_plugin_version.tscn") + var check_version_instance : Node = check_version_scene.instantiate() + check_version_instance.auto_start = true + check_version_instance.new_version_detected.connect(_add_update_plugin_tool_option) + add_child(check_version_instance) + +func open_update_plugin() -> void: + var update_plugin_scene : PackedScene = load(get_plugin_path() + "installer/update_plugin.tscn") + var update_plugin_instance : Node = update_plugin_scene.instantiate() + update_plugin_instance.auto_start = true + update_plugin_instance.update_completed.connect(_remove_update_plugin_tool_option) + add_child(update_plugin_instance) + +func open_setup_wizard() -> void: + var setup_wizard_scene : PackedScene = load(get_plugin_path() + "installer/setup_wizard.tscn") + var setup_wizard_instance : Node = setup_wizard_scene.instantiate() + add_child(setup_wizard_instance) + +func _add_update_plugin_tool_option(new_version : String) -> void: + update_plugin_tool_string = "Update %s to v%s..." % [get_plugin_name(), new_version] + add_tool_menu_item(update_plugin_tool_string, open_update_plugin) + +func _remove_update_plugin_tool_option() -> void: + if update_plugin_tool_string.is_empty(): return + remove_tool_menu_item(update_plugin_tool_string) + update_plugin_tool_string = "" + +func _show_plugin_dialogues() -> void: + if ProjectSettings.has_setting(PROJECT_SETTINGS_PATH + "disable_install_wizard") : + if ProjectSettings.get_setting(PROJECT_SETTINGS_PATH + "disable_install_wizard") : + return + _open_confirmation_dialog() + ProjectSettings.set_setting(PROJECT_SETTINGS_PATH + "disable_install_wizard", true) + ProjectSettings.save() + +func _resave_if_recently_opened() -> void: + if Engine.get_physics_frames() < MAX_PHYSICS_FRAMES_FROM_START: + var timer: Timer = Timer.new() + var callable := func(): + if Engine.get_frames_per_second() >= 10: + timer.stop() + EditorInterface.save_scene() + timer.queue_free() + timer.timeout.connect(callable) + add_child(timer) + timer.start(OPEN_EDITOR_DELAY) + +func _add_audio_bus(bus_name : String) -> void: + var has_bus_name := false + for bus_idx in range(AudioServer.bus_count): + var existing_bus_name := AudioServer.get_bus_name(bus_idx) + if existing_bus_name == bus_name: + has_bus_name = true + break + if not has_bus_name: + AudioServer.add_bus() + var new_bus_idx := AudioServer.bus_count - 1 + AudioServer.set_bus_name(new_bus_idx, bus_name) + AudioServer.set_bus_send(new_bus_idx, &"Master") + ProjectSettings.save() + +func _install_audio_busses() -> void: + if ProjectSettings.has_setting(PROJECT_SETTINGS_PATH + "disable_install_audio_busses"): + if ProjectSettings.get_setting(PROJECT_SETTINGS_PATH + "disable_install_audio_busses") : + return + _add_audio_bus("Music") + _add_audio_bus("SFX") + ProjectSettings.set_setting(PROJECT_SETTINGS_PATH + "disable_install_audio_busses", true) + ProjectSettings.save() + +func _add_tool_options() -> void: + add_tool_menu_item("Run " + get_plugin_name() + " Setup...", open_setup_wizard) + _open_check_plugin_version() + +func _remove_tool_options() -> void: + remove_tool_menu_item("Run " + get_plugin_name() + " Setup...") + _remove_update_plugin_tool_option() + +func _enter_tree() -> void: + _install_audio_busses() + _add_tool_options() + _add_translations() + _show_plugin_dialogues() + _resave_if_recently_opened() + instance = self + +func _exit_tree() -> void: + _remove_tool_options() + instance = null diff --git a/addons/maaacks_game_template/plugin.gd.uid b/addons/maaacks_game_template/plugin.gd.uid new file mode 100644 index 0000000..2b07b65 --- /dev/null +++ b/addons/maaacks_game_template/plugin.gd.uid @@ -0,0 +1 @@ +uid://bndaaa5when2r diff --git a/addons/maaacks_game_template/utilities/api_client.gd b/addons/maaacks_game_template/utilities/api_client.gd new file mode 100644 index 0000000..a15394d --- /dev/null +++ b/addons/maaacks_game_template/utilities/api_client.gd @@ -0,0 +1,156 @@ +@tool +extends Node +## Node for sending a requesti to an API endpoint for a JSON-encoded response. + +signal response_received(response_body) +signal request_failed(error) + +const RESULT_CANT_CONNECT = "Failed to connect" +const RESULT_CANT_RESOLVE = "Failed to resolve" +const RESULT_CONNECTION_ERROR = "Connection error" +const RESULT_TIMEOUT = "Connection timeout" +const RESULT_SERVER_ERROR = "Server error" +const REQUEST_FAILED = "Error in the request" +const REQUEST_TIMEOUT = "Request timed out on the client side" +const URL_NOT_SET = "URL parameter is not set" +const PARSE_FAILED = "Parsing failed" + +## Location of the API endpoint. +@export var api_url : String +## HTTP request method to use. Typically GET or POST. +@export var request_method : HTTPClient.Method = HTTPClient.METHOD_POST +@export_group("Advanced") +## Location of an API key file, if authorization is required by the endpoint. +@export_file("*.txt") var api_key_file : String +## Time in seconds before the request fails due to timeout. +@export var request_timeout : float = 0.0 +## If true, test sending a request. +## Replace with @export_tool_button for Godot 4.4+ +@export var _send_request_action : bool = false : + set(value): + if value and Engine.is_editor_hint(): + request() +# For Godot 4.4+ +# @export_tool_button("Send Request") var _send_request_action = request + + +@onready var _http_request : HTTPRequest = $HTTPRequest +@onready var _timeout_timer : Timer= $TimeoutTimer + +## State flag for whether the connection has timed out on the client-side. +var timed_out : bool = false + +func get_http_request() -> HTTPRequest: + return _http_request + +func get_api_key() -> String: + if api_key_file.is_empty(): + return "" + var file := FileAccess.open(api_key_file, FileAccess.READ) + var error := FileAccess.get_open_error() + if error != OK: + push_error("API Key reading error: %d" % error) + return "" + var content = file.get_as_text() + file.close() + return content + +func get_api_url() -> String: + return api_url + +func get_api_method() -> int: + return request_method + +func mock_empty_body() -> String: + var form : Dictionary = {} + return JSON.stringify(form) + +func mock_request(body : String): + await(get_tree().create_timer(10.0).timeout) + _on_request_completed(HTTPRequest.RESULT_SUCCESS, "200", [], body) + +func request(body : String = "", request_headers : Array = []) -> void: + var local_http_request : HTTPRequest = get_http_request() + var key : String = get_api_key() + var url : String = get_api_url() + var method : int = get_api_method() + if url.is_empty(): + request_failed.emit(URL_NOT_SET) + push_error(URL_NOT_SET) + return + request_headers.append("Content-Type: application/json") + if key: + request_headers.append("x-api-key: %s" % key) + if request_timeout > 0.0: + local_http_request.timeout = request_timeout + var error = local_http_request.request(url, request_headers, method, body) + if error != OK: + request_failed.emit(REQUEST_FAILED) + push_error("HTTP Request error: %d" % error) + return + if request_timeout > 0.0: + _timeout_timer.start(request_timeout + 1.0) + +func request_raw(data : PackedByteArray = [], request_headers : Array = []) -> void: + var local_http_request : HTTPRequest = get_http_request() + var key : String = get_api_key() + var url : String = get_api_url() + var method : int = get_api_method() + if url.is_empty(): + request_failed.emit(URL_NOT_SET) + push_error(URL_NOT_SET) + return + request_headers.append("Content-Type: application/json") + if key: + request_headers.append("x-api-key: %s" % key) + if request_timeout > 0.0: + local_http_request.timeout = request_timeout + var error = local_http_request.request_raw(url, request_headers, method, data) + if error != OK: + request_failed.emit(REQUEST_FAILED) + push_error("HTTP Request error: %d" % error) + return + if request_timeout > 0.0: + _timeout_timer.start(request_timeout + 1.0) + +func _on_request_completed(result, response_code, headers, body) -> void: + # If already timed out on client-side, then return. + if timed_out: return + _timeout_timer.stop() + if result == HTTPRequest.RESULT_SUCCESS: + var body_string : String + if body is PackedByteArray: + body_string = body.get_string_from_utf8() + elif body is String: + body_string = body + var json := JSON.new() + var error = json.parse(body_string) + if error != OK: + request_failed.emit(PARSE_FAILED) + push_error("Parse error: %d" % error) + return + var parsed_data = json.data + response_received.emit(json.data) + else: + var error_message : String + match(result): + HTTPRequest.RESULT_CANT_CONNECT: + error_message = RESULT_CANT_CONNECT + HTTPRequest.RESULT_CANT_RESOLVE: + error_message = RESULT_CANT_RESOLVE + HTTPRequest.RESULT_CONNECTION_ERROR: + error_message = RESULT_CONNECTION_ERROR + HTTPRequest.RESULT_TIMEOUT: + error_message = RESULT_TIMEOUT + _: + error_message = RESULT_SERVER_ERROR + request_failed.emit(error_message) + push_error("HTTP Result error: %d" % result) + +func _on_http_request_request_completed(result, response_code, headers, body) -> void: + _on_request_completed(result, response_code, headers, body) + +func _on_timeout_timer_timeout() -> void: + timed_out = true + request_failed.emit(REQUEST_TIMEOUT) + push_warning(REQUEST_TIMEOUT) diff --git a/addons/maaacks_game_template/utilities/api_client.gd.uid b/addons/maaacks_game_template/utilities/api_client.gd.uid new file mode 100644 index 0000000..9af7769 --- /dev/null +++ b/addons/maaacks_game_template/utilities/api_client.gd.uid @@ -0,0 +1 @@ +uid://xw0i0piu0cax diff --git a/addons/maaacks_game_template/utilities/api_client.tscn b/addons/maaacks_game_template/utilities/api_client.tscn new file mode 100644 index 0000000..0f3cdc0 --- /dev/null +++ b/addons/maaacks_game_template/utilities/api_client.tscn @@ -0,0 +1,13 @@ +[gd_scene load_steps=2 format=3 uid="uid://cf0hkngq1mgfy"] + +[ext_resource type="Script" uid="uid://xw0i0piu0cax" path="res://addons/maaacks_game_template/utilities/api_client.gd" id="1_c5ofg"] + +[node name="APIClient" type="Node"] +script = ExtResource("1_c5ofg") + +[node name="HTTPRequest" type="HTTPRequest" parent="."] + +[node name="TimeoutTimer" type="Timer" parent="."] + +[connection signal="request_completed" from="HTTPRequest" to="." method="_on_http_request_request_completed"] +[connection signal="timeout" from="TimeoutTimer" to="." method="_on_timeout_timer_timeout"] diff --git a/addons/maaacks_game_template/utilities/download_and_extract.gd b/addons/maaacks_game_template/utilities/download_and_extract.gd new file mode 100644 index 0000000..49985d4 --- /dev/null +++ b/addons/maaacks_game_template/utilities/download_and_extract.gd @@ -0,0 +1,284 @@ +@tool +## Utility node for downloading and unzipping a file from a URL to an extraction destination. +extends Node + +## Sent when the run has completed. +signal run_completed +## Sent when a response is received from the server. +signal response_received(response_body) +## Sent when the run has failed or exited early for any reason. +signal run_failed(error : String) +## Sent when the zip file has finished saving. +signal zip_saved + +const TEMPORARY_ZIP_PATH = "res://temp.zip" +const RESULT_CANT_CONNECT = "Failed to connect" +const RESULT_CANT_RESOLVE = "Failed to resolve" +const RESULT_CONNECTION_ERROR = "Connection error" +const RESULT_TIMEOUT = "Connection timeout" +const RESULT_SERVER_ERROR = "Server error" +const REQUEST_FAILED = "Error in the request" +const REQUEST_TIMEOUT = "Request timed out on the client side" +const DOWNLOAD_IN_PROGRESS = "Download already in progress" +const EXTRACT_IN_PROGRESS = "Extract already in progress" +const DELETE_IN_PROGRESS = "Delete already in progress" +const FAILED_TO_SAVE_ZIP_FILE = "Failed to save the zip file" +const FAILED_TO_MAKE_EXTRACT_DIR = "Failed to make extract directory" +const FAILED_TO_READ_ZIP_FILE = "Failed to read the zip file" +const DOWNLOADED_ZIP_FILE_DOESNT_EXIST = "The downloaded ZIP file doesn't exist" +const URL_NOT_SET = "URL parameter is not set" + +enum DownloadAndExtractStage{ + NONE, + DOWNLOAD, + SAVE, + EXTRACT, + DELETE, +} + +## Location of the zip file to be downloaded. +@export var zip_url : String +## Path where the zipped files are to be extracted. +@export_dir var extract_path : String +@export_group("Advanced") +## If not empty, zipped file paths that do not contain a match to the string will be ignored. +@export var path_match_string : String = "" +## Assuming zip file contains a single base directory, the flag copies all of the contents, +## as if they were at the base of the zip file. It never makes the base directory locally. +@export var skip_base_zip_dir : bool = false +## Forces a download and extraction even if the files already exist. +@export var force : bool = false +## Path where the zip file will be stored. +@export var zip_file_path : String = TEMPORARY_ZIP_PATH +## If true, delete a downloaded zip file after the contents are extracted. +@export var delete_zip_file : bool = true +## Ratio of processing time that should be spent on extracting files. +@export_range(0.0, 1.0) var process_time_ratio : float = 0.75 +## Seconds of delay added between saving the zip file and extracting it. +@export_range(0.0, 3.0) var extraction_delay : float = 0.25 +## Duration to wait before the request times out. +@export var request_timeout : float = 0.0 +@export var _start_run_action : bool = false : + set(value): + if value and Engine.is_editor_hint(): + run() +# For Godot 4.4 +# @export_tool_button("Download & Extract") var _start_run_action = run + + +@onready var _http_request : HTTPRequest = $HTTPRequest +@onready var _timeout_timer : Timer= $TimeoutTimer + +## State flag for whether the connection has timed out on the client-side. +var timed_out : bool = false +## Current stage of the download and extract process. +var stage : DownloadAndExtractStage = DownloadAndExtractStage.NONE +var zip_reader : ZIPReader = ZIPReader.new() +var zipped_file_paths : PackedStringArray = [] +var extracted_file_paths : Array[String] = [] +var skipped_file_paths : Array[String] = [] +var downloaded_zip_file : bool = false +var base_zip_path : String = "" +var _save_progress : float = 0.0 + +func get_http_request() -> HTTPRequest: + return _http_request + +func get_zip_url() -> String: + return zip_url + +func _zip_exists() -> bool: + return FileAccess.file_exists(zip_file_path) + +func get_request_method() -> int: + return HTTPClient.METHOD_GET + +## Sends the request to download the target zip file, and then extracts the contents. +func run(request_headers : Array = []) -> void: + if stage == DownloadAndExtractStage.DOWNLOAD: + run_failed.emit(DOWNLOAD_IN_PROGRESS) + push_warning(DOWNLOAD_IN_PROGRESS) + return + if _zip_exists() and not force: + _extract_files.call_deferred() + return + var local_http_request : HTTPRequest = get_http_request() + var url : String = get_zip_url() + var method : int = get_request_method() + if url.is_empty(): + run_failed.emit(URL_NOT_SET) + push_error(URL_NOT_SET) + return + if request_timeout > 0.0: + local_http_request.timeout = request_timeout + var error = local_http_request.request(url, request_headers, method) + if error != OK: + run_failed.emit(REQUEST_FAILED) + push_error("HTTP Request error: %d" % error) + return + if request_timeout > 0.0: + _timeout_timer.start(request_timeout + 1.0) + stage = DownloadAndExtractStage.DOWNLOAD + +func _delete_zip_file() -> void: + if not delete_zip_file or not downloaded_zip_file: return + if stage == DownloadAndExtractStage.DELETE: + run_failed.emit(DELETE_IN_PROGRESS) + push_warning(DELETE_IN_PROGRESS) + return + stage = DownloadAndExtractStage.DELETE + DirAccess.remove_absolute(zip_file_path) + downloaded_zip_file = false + +func _save_zip_file(body : PackedByteArray) -> void: + stage = DownloadAndExtractStage.SAVE + var file = FileAccess.open(zip_file_path, FileAccess.WRITE) + if not file: + run_failed.emit(FAILED_TO_SAVE_ZIP_FILE) + push_error(FAILED_TO_SAVE_ZIP_FILE) + return + file.store_buffer(body) + file.close() + downloaded_zip_file = true + zip_saved.emit() + +func extract_path_exists() -> bool: + return DirAccess.dir_exists_absolute(extract_path) + +func _make_extract_path() -> void: + var err := DirAccess.make_dir_recursive_absolute(extract_path) + if err != OK: + run_failed.emit(FAILED_TO_MAKE_EXTRACT_DIR) + push_error(FAILED_TO_MAKE_EXTRACT_DIR) + +func _extract_files() -> void: + if stage == DownloadAndExtractStage.EXTRACT: + run_failed.emit(EXTRACT_IN_PROGRESS) + push_warning(EXTRACT_IN_PROGRESS) + return + stage = DownloadAndExtractStage.EXTRACT + if not _zip_exists(): + run_failed.emit(DOWNLOADED_ZIP_FILE_DOESNT_EXIST) + push_error(DOWNLOADED_ZIP_FILE_DOESNT_EXIST) + return + if not extract_path_exists(): _make_extract_path() + var error = zip_reader.open(zip_file_path) + if error != OK: + run_failed.emit(FAILED_TO_READ_ZIP_FILE) + push_error("ZIP Reader error: %d" % error) + return + zipped_file_paths = zip_reader.get_files() + if skip_base_zip_dir: + base_zip_path = zipped_file_paths[0] + if not base_zip_path.ends_with("/"): + push_warning("Skipping extracting base path, but it is not a directory.") + zipped_file_paths.remove_at(0) + +func _on_request_completed(result, response_code, headers, body) -> void: + # If already timed out on client-side, then return. + if timed_out: return + _timeout_timer.stop() + if _zip_exists(): _delete_zip_file() + if result == HTTPRequest.RESULT_SUCCESS: + if body is PackedByteArray: + response_received.emit(body) + _save_zip_file(body) + _save_progress = 0.0 + var tween = create_tween() + tween.tween_property(self, "_save_progress", 1.0, extraction_delay) + await tween.finished + _extract_files.call_deferred() + else: + var error_message : String + match(result): + HTTPRequest.RESULT_CANT_CONNECT: + error_message = RESULT_CANT_CONNECT + HTTPRequest.RESULT_CANT_RESOLVE: + error_message = RESULT_CANT_RESOLVE + HTTPRequest.RESULT_CONNECTION_ERROR: + error_message = RESULT_CONNECTION_ERROR + HTTPRequest.RESULT_TIMEOUT: + error_message = RESULT_TIMEOUT + _: + error_message = RESULT_SERVER_ERROR + run_failed.emit(error_message) + push_error("HTTP Result error: %d" % result) + +func _on_http_request_request_completed(result, response_code, headers, body) -> void: + _on_request_completed(result, response_code, headers, body) + +func _on_timeout_timer_timeout() -> void: + timed_out = true + run_failed.emit(REQUEST_TIMEOUT) + push_warning(REQUEST_TIMEOUT) + +func get_progress() -> float: + if stage == DownloadAndExtractStage.DOWNLOAD: + return get_download_progress() + elif stage == DownloadAndExtractStage.SAVE: + return get_save_progress() + elif stage == DownloadAndExtractStage.EXTRACT: + return get_extraction_progress() + return 0.0 + +func get_save_progress() -> float: + return _save_progress + +func get_extraction_progress() -> float: + if zipped_file_paths.size() == 0: + return 0.0 + return float(extracted_file_paths.size()) / float(zipped_file_paths.size()) + +func get_download_progress() -> float: + var body_size := _http_request.get_body_size() + if body_size < 1: return 0.0 + return float(_http_request.get_downloaded_bytes()) / float(body_size) + +func _zipped_files_remaining() -> int: + return zipped_file_paths.size() - (extracted_file_paths.size() + skipped_file_paths.size()) + +func _extract_next_zipped_file() -> void: + var path_index = extracted_file_paths.size() + skipped_file_paths.size() + var zipped_file_path := zipped_file_paths[path_index] + if path_match_string and not zipped_file_path.contains(path_match_string): + skipped_file_paths.append(zipped_file_path) + return + var extract_path_dir := extract_path + if not extract_path_dir.ends_with("/"): + extract_path_dir += "/" + var full_path := extract_path_dir + if skip_base_zip_dir: + full_path += zipped_file_path.replace(base_zip_path, "") + else: + full_path += zipped_file_path + if full_path.ends_with("/"): + if not DirAccess.dir_exists_absolute(full_path): + DirAccess.make_dir_recursive_absolute(full_path) + else: + if not FileAccess.file_exists(full_path) or force: + var file_access := FileAccess.open(full_path, FileAccess.WRITE) + if file_access == null: + skipped_file_paths.append(zipped_file_path) + push_error("Failed to open file: %s" % full_path) + return + var file_contents = zip_reader.read_file(zipped_file_path) + file_access.store_buffer(file_contents) + file_access.close() + extracted_file_paths.append(full_path) + +func _finish_extraction() -> void: + zip_reader.close() + _delete_zip_file() + stage = DownloadAndExtractStage.NONE + run_completed.emit() + +func _process(delta : float) -> void: + if stage == DownloadAndExtractStage.EXTRACT: + var frame_start_time : float = Time.get_unix_time_from_system() + var frame_time : float = 0.0 + while (frame_time < delta * process_time_ratio): + if _zipped_files_remaining() == 0: + _finish_extraction() + break + _extract_next_zipped_file() + frame_time = Time.get_unix_time_from_system() - frame_start_time diff --git a/addons/maaacks_game_template/utilities/download_and_extract.gd.uid b/addons/maaacks_game_template/utilities/download_and_extract.gd.uid new file mode 100644 index 0000000..bd00f4d --- /dev/null +++ b/addons/maaacks_game_template/utilities/download_and_extract.gd.uid @@ -0,0 +1 @@ +uid://bkno1by7i3hrb diff --git a/addons/maaacks_game_template/utilities/download_and_extract.tscn b/addons/maaacks_game_template/utilities/download_and_extract.tscn new file mode 100644 index 0000000..af3c542 --- /dev/null +++ b/addons/maaacks_game_template/utilities/download_and_extract.tscn @@ -0,0 +1,14 @@ +[gd_scene load_steps=2 format=3 uid="uid://5hhnbgqjwnic"] + +[ext_resource type="Script" uid="uid://bkno1by7i3hrb" path="res://addons/maaacks_game_template/utilities/download_and_extract.gd" id="1_1few7"] + +[node name="DownloadAndExtract" type="Node"] +script = ExtResource("1_1few7") + +[node name="HTTPRequest" type="HTTPRequest" parent="."] + +[node name="TimeoutTimer" type="Timer" parent="."] +one_shot = true + +[connection signal="request_completed" from="HTTPRequest" to="." method="_on_http_request_request_completed"] +[connection signal="timeout" from="TimeoutTimer" to="." method="_on_timeout_timer_timeout"] diff --git a/default_bus_layout.tres b/default_bus_layout.tres new file mode 100644 index 0000000..2967d93 --- /dev/null +++ b/default_bus_layout.tres @@ -0,0 +1,15 @@ +[gd_resource type="AudioBusLayout" format=3 uid="uid://ch8r6u24fgg03"] + +[resource] +bus/1/name = &"Music" +bus/1/solo = false +bus/1/mute = false +bus/1/bypass_fx = false +bus/1/volume_db = 0.0 +bus/1/send = &"Master" +bus/2/name = &"SFX" +bus/2/solo = false +bus/2/mute = false +bus/2/bypass_fx = false +bus/2/volume_db = 0.0 +bus/2/send = &"Master" diff --git a/menus/assets/git_logo/Git-Logo-2Color.png b/menus/assets/git_logo/Git-Logo-2Color.png new file mode 100644 index 0000000..18c5b29 Binary files /dev/null and b/menus/assets/git_logo/Git-Logo-2Color.png differ diff --git a/menus/assets/git_logo/Git-Logo-2Color.png.import b/menus/assets/git_logo/Git-Logo-2Color.png.import new file mode 100644 index 0000000..b8ce03f --- /dev/null +++ b/menus/assets/git_logo/Git-Logo-2Color.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://buql3bcbqx4pp" +path="res://.godot/imported/Git-Logo-2Color.png-8d60985b349b90d009d8e0fa064f2f30.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://menus/assets/git_logo/Git-Logo-2Color.png" +dest_files=["res://.godot/imported/Git-Logo-2Color.png-8d60985b349b90d009d8e0fa064f2f30.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/menus/assets/git_logo/LICENSE.txt b/menus/assets/git_logo/LICENSE.txt new file mode 100644 index 0000000..2d17b1d --- /dev/null +++ b/menus/assets/git_logo/LICENSE.txt @@ -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/ + diff --git a/menus/assets/godot_engine_logo/LICENSE.txt b/menus/assets/godot_engine_logo/LICENSE.txt new file mode 100644 index 0000000..a081c9e --- /dev/null +++ b/menus/assets/godot_engine_logo/LICENSE.txt @@ -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/ \ No newline at end of file diff --git a/menus/assets/godot_engine_logo/logo_vertical_color_dark.png b/menus/assets/godot_engine_logo/logo_vertical_color_dark.png new file mode 100644 index 0000000..2c38732 Binary files /dev/null and b/menus/assets/godot_engine_logo/logo_vertical_color_dark.png differ diff --git a/menus/assets/godot_engine_logo/logo_vertical_color_dark.png.import b/menus/assets/godot_engine_logo/logo_vertical_color_dark.png.import new file mode 100644 index 0000000..5e1f61c --- /dev/null +++ b/menus/assets/godot_engine_logo/logo_vertical_color_dark.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://4hasb7yih53v" +path="res://.godot/imported/logo_vertical_color_dark.png-384a2af4f2358a96a64d672d7da75a7a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://menus/assets/godot_engine_logo/logo_vertical_color_dark.png" +dest_files=["res://.godot/imported/logo_vertical_color_dark.png-384a2af4f2358a96a64d672d7da75a7a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/menus/assets/plugin_logo/LICENSE.txt b/menus/assets/plugin_logo/LICENSE.txt new file mode 100644 index 0000000..0cad3a2 --- /dev/null +++ b/menus/assets/plugin_logo/LICENSE.txt @@ -0,0 +1,5 @@ +Maaack's Minimal Game Template Logo +Copyright (c) Marek Belski + +This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International +license (CC BY-NC-ND 4.0 International): https://creativecommons.org/licenses/by-nc-nd/4.0 diff --git a/menus/assets/plugin_logo/logo.png b/menus/assets/plugin_logo/logo.png new file mode 100644 index 0000000..f509071 Binary files /dev/null and b/menus/assets/plugin_logo/logo.png differ diff --git a/menus/assets/plugin_logo/logo.png.import b/menus/assets/plugin_logo/logo.png.import new file mode 100644 index 0000000..f6bb434 --- /dev/null +++ b/menus/assets/plugin_logo/logo.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b760ux4aanw3h" +path="res://.godot/imported/logo.png-1b8f877e8e13c68d267e1d993dfb49fd.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://menus/assets/plugin_logo/logo.png" +dest_files=["res://.godot/imported/logo.png-1b8f877e8e13c68d267e1d993dfb49fd.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/menus/resources/themes/expedition.tres b/menus/resources/themes/expedition.tres new file mode 100644 index 0000000..530d14e --- /dev/null +++ b/menus/resources/themes/expedition.tres @@ -0,0 +1,98 @@ +[gd_resource type="Theme" format=3 uid="uid://bbvjs5vfhsn4p"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_g0sbc"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.823636, 0.744991, 0.659007, 1) +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.662913, 0.549096, 0.478248, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_krvwn"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.662913, 0.549096, 0.478248, 1) +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.466529, 0.360525, 0.333165, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_gfyr3"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.466529, 0.360525, 0.333165, 1) +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.282353, 0.231067, 0.227161, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_tytr8"] +bg_color = Color(0.282353, 0.231067, 0.227161, 1) +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.133364, 0.133364, 0.133364, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_wsakr"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.823636, 0.744991, 0.659007, 1) +border_width_right = 2 +border_color = Color(0.662913, 0.549096, 0.478248, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_1ngrn"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.662913, 0.549096, 0.478248, 1) +border_width_right = 2 +border_color = Color(0.466529, 0.360525, 0.333165, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_q0g5m"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.466529, 0.360525, 0.333165, 1) +border_width_top = 2 +border_width_right = 2 +border_color = Color(0.282353, 0.231067, 0.227161, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 + +[resource] +Button/styles/hover = SubResource("StyleBoxFlat_g0sbc") +Button/styles/normal = SubResource("StyleBoxFlat_krvwn") +Button/styles/pressed = SubResource("StyleBoxFlat_gfyr3") +Panel/styles/panel = SubResource("StyleBoxFlat_tytr8") +PanelContainer/styles/panel = SubResource("StyleBoxFlat_tytr8") +TabContainer/styles/panel = SubResource("StyleBoxFlat_tytr8") +TabContainer/styles/tab_hovered = SubResource("StyleBoxFlat_wsakr") +TabContainer/styles/tab_selected = SubResource("StyleBoxFlat_1ngrn") +TabContainer/styles/tab_unselected = SubResource("StyleBoxFlat_q0g5m") diff --git a/menus/resources/themes/gravity.tres b/menus/resources/themes/gravity.tres new file mode 100644 index 0000000..42f820a --- /dev/null +++ b/menus/resources/themes/gravity.tres @@ -0,0 +1,116 @@ +[gd_resource type="Theme" format=3 uid="uid://coeyx2tnur752"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_w50h3"] +content_margin_left = 16.0 +content_margin_top = 4.0 +content_margin_right = 16.0 +content_margin_bottom = 4.0 +bg_color = Color(0.125911, 0.125911, 0.125911, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.95, 0.95, 0.95, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 +corner_detail = 1 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_6tkof"] +content_margin_left = 16.0 +content_margin_top = 4.0 +content_margin_right = 16.0 +content_margin_bottom = 4.0 +bg_color = Color(0, 0, 0, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.95, 0.95, 0.95, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 +corner_detail = 1 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ftays"] +content_margin_left = 16.0 +content_margin_top = 4.0 +content_margin_right = 16.0 +content_margin_bottom = 4.0 +bg_color = Color(0.95, 0.95, 0.95, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.125911, 0.125911, 0.125911, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 +corner_detail = 1 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_sucf2"] +bg_color = Color(0.0619267, 0.0619267, 0.0619266, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 +corner_detail = 1 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_u30hj"] +content_margin_left = 8.0 +content_margin_right = 8.0 +bg_color = Color(0.125536, 0.125536, 0.125536, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_color = Color(0.945281, 0.945281, 0.945281, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_detail = 1 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_h5y6c"] +content_margin_left = 8.0 +content_margin_right = 8.0 +bg_color = Color(0.945281, 0.945281, 0.945281, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_color = Color(0.0195315, 0.0195315, 0.0195315, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_detail = 1 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_g0qsc"] +content_margin_left = 8.0 +content_margin_right = 8.0 +bg_color = Color(0, 0, 0, 0.933333) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_color = Color(0.945281, 0.945281, 0.945281, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_detail = 1 + +[resource] +Button/colors/font_color = Color(0.95, 0.95, 0.95, 1) +Button/colors/font_focus_color = Color(0.95, 0.95, 0.95, 1) +Button/colors/font_hover_color = Color(0.95, 0.95, 0.95, 1) +Button/colors/font_pressed_color = Color(0.125911, 0.125911, 0.125911, 1) +Button/styles/disabled = null +Button/styles/focus = null +Button/styles/hover = SubResource("StyleBoxFlat_w50h3") +Button/styles/normal = SubResource("StyleBoxFlat_6tkof") +Button/styles/pressed = SubResource("StyleBoxFlat_ftays") +Panel/styles/panel = SubResource("StyleBoxFlat_sucf2") +PanelContainer/styles/panel = SubResource("StyleBoxFlat_sucf2") +TabContainer/colors/font_hovered_color = Color(0.945281, 0.945281, 0.945281, 1) +TabContainer/colors/font_selected_color = Color(0.0195315, 0.0195315, 0.0195315, 1) +TabContainer/colors/font_unselected_color = Color(0.945281, 0.945281, 0.945281, 1) +TabContainer/styles/panel = SubResource("StyleBoxFlat_sucf2") +TabContainer/styles/tab_hovered = SubResource("StyleBoxFlat_u30hj") +TabContainer/styles/tab_selected = SubResource("StyleBoxFlat_h5y6c") +TabContainer/styles/tab_unselected = SubResource("StyleBoxFlat_g0qsc") diff --git a/menus/resources/themes/grow.tres b/menus/resources/themes/grow.tres new file mode 100644 index 0000000..e163bd2 --- /dev/null +++ b/menus/resources/themes/grow.tres @@ -0,0 +1,98 @@ +[gd_resource type="Theme" format=3 uid="uid://c55qwm6y3ah6r"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_g0sbc"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.428961, 0.730226, 0.50528, 1) +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.298858, 0.546296, 0.363635, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_krvwn"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.298858, 0.546296, 0.363635, 1) +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.234842, 0.443383, 0.289887, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_gfyr3"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.234842, 0.443383, 0.289887, 1) +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.234842, 0.443383, 0.289887, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_tytr8"] +bg_color = Color(0.191138, 0.370484, 0.238651, 1) +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.115392, 0.241196, 0.148848, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_wsakr"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.428961, 0.730226, 0.50528, 1) +border_width_right = 2 +border_color = Color(0.298858, 0.546296, 0.363635, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_1ngrn"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.298858, 0.546296, 0.363635, 1) +border_width_right = 2 +border_color = Color(0.234842, 0.443383, 0.289887, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_q0g5m"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.234842, 0.443383, 0.289887, 1) +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.234842, 0.443383, 0.289887, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 + +[resource] +Button/styles/hover = SubResource("StyleBoxFlat_g0sbc") +Button/styles/normal = SubResource("StyleBoxFlat_krvwn") +Button/styles/pressed = SubResource("StyleBoxFlat_gfyr3") +Panel/styles/panel = SubResource("StyleBoxFlat_tytr8") +PanelContainer/styles/panel = SubResource("StyleBoxFlat_tytr8") +TabContainer/styles/panel = SubResource("StyleBoxFlat_tytr8") +TabContainer/styles/tab_hovered = SubResource("StyleBoxFlat_wsakr") +TabContainer/styles/tab_selected = SubResource("StyleBoxFlat_1ngrn") +TabContainer/styles/tab_unselected = SubResource("StyleBoxFlat_q0g5m") diff --git a/menus/resources/themes/lab.tres b/menus/resources/themes/lab.tres new file mode 100644 index 0000000..e0b72e4 --- /dev/null +++ b/menus/resources/themes/lab.tres @@ -0,0 +1,185 @@ +[gd_resource type="Theme" format=3 uid="uid://uenhweg3q2xr"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_4b4gg"] +content_margin_left = 12.0 +content_margin_right = 12.0 +content_margin_bottom = 14.0 +bg_color = Color(0.305882, 0.454902, 0.6, 1) +border_width_left = 4 +border_width_top = 4 +border_width_right = 4 +border_width_bottom = 12 +border_color = Color(0.141176, 0.321569, 0.45098, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_l51yu"] +content_margin_left = 12.0 +content_margin_right = 12.0 +content_margin_bottom = 14.0 +bg_color = Color(0.243137, 0.25098, 0.333333, 1) +border_width_left = 4 +border_width_top = 4 +border_width_right = 4 +border_width_bottom = 12 +border_color = Color(0.219608, 0.176471, 0.207843, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_e7ejt"] +content_margin_left = 12.0 +content_margin_right = 12.0 +content_margin_bottom = 14.0 +bg_color = Color(0.121569, 0.176471, 0.211765, 1) +border_width_left = 4 +border_width_top = 4 +border_width_right = 4 +border_width_bottom = 12 +border_color = Color(0.0784314, 0.121569, 0.145098, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ywrjj"] +bg_color = Color(0.6, 0.6, 0.6, 0) +border_width_left = 3 +border_width_top = 3 +border_width_bottom = 3 +corner_radius_top_left = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_tyerd"] +bg_color = Color(0.8, 0.8, 0.8, 1) +border_width_left = 2 +border_width_top = 3 +border_width_bottom = 3 +border_color = Color(0.8, 0.8, 0.8, 0) +corner_radius_top_left = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ulcur"] +content_margin_left = 8.0 +content_margin_top = 8.0 +content_margin_right = 8.0 +content_margin_bottom = 8.0 +bg_color = Color(0.0784314, 0.121569, 0.145098, 1) +border_width_left = 4 +border_width_top = 4 +border_width_right = 4 +border_width_bottom = 4 +border_color = Color(0.0431373, 0.0627451, 0.0862745, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_aliwb"] +bg_color = Color(0.6, 0.6, 0.6, 0) +border_width_top = 3 +border_width_right = 3 +border_width_bottom = 3 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_tnl2j"] +content_margin_bottom = 3.0 +bg_color = Color(0.8, 0.8, 0.8, 1) +border_width_top = 3 +border_width_right = 3 +border_width_bottom = 3 +border_color = Color(0.8, 0.8, 0.8, 0) +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_k5faf"] +bg_color = Color(0.6, 0.6, 0.6, 0) +border_width_left = 3 +border_width_top = 3 +border_width_right = 3 +border_width_bottom = 3 +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_5j0a2"] +bg_color = Color(0.8, 0.8, 0.8, 1) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_odiue"] +content_margin_left = 8.0 +content_margin_top = 8.0 +content_margin_right = 8.0 +content_margin_bottom = 8.0 +bg_color = Color(0.0784314, 0.121569, 0.145098, 1) +border_width_left = 4 +border_width_top = 4 +border_width_right = 4 +border_width_bottom = 4 +border_color = Color(0.219608, 0.176471, 0.207843, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ghjya"] +content_margin_left = 12.0 +content_margin_right = 12.0 +bg_color = Color(0.305882, 0.454902, 0.6, 1) +border_width_left = 4 +border_width_top = 4 +border_width_right = 4 +border_color = Color(0.141176, 0.321569, 0.45098, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_m6y06"] +content_margin_left = 12.0 +content_margin_right = 12.0 +bg_color = Color(0.243137, 0.25098, 0.333333, 1) +border_width_left = 4 +border_width_top = 4 +border_width_right = 4 +border_color = Color(0.219608, 0.176471, 0.207843, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_5icga"] +content_margin_left = 12.0 +content_margin_right = 12.0 +bg_color = Color(0.0784314, 0.121569, 0.145098, 1) +border_width_left = 4 +border_width_top = 4 +border_width_right = 4 +border_color = Color(0.121569, 0.176471, 0.211765, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 + +[sub_resource type="FontVariation" id="FontVariation_i860b"] +spacing_top = 4 +spacing_bottom = 2 + +[resource] +default_font = SubResource("FontVariation_i860b") +Button/styles/hover = SubResource("StyleBoxFlat_4b4gg") +Button/styles/normal = SubResource("StyleBoxFlat_l51yu") +Button/styles/pressed = SubResource("StyleBoxFlat_e7ejt") +LeftStaminaBar/base_type = &"ProgressBar" +LeftStaminaBar/styles/background = SubResource("StyleBoxFlat_ywrjj") +LeftStaminaBar/styles/fill = SubResource("StyleBoxFlat_tyerd") +Panel/styles/panel = SubResource("StyleBoxFlat_ulcur") +PanelContainer/styles/panel = SubResource("StyleBoxFlat_ulcur") +RightStaminaBar/base_type = &"ProgressBar" +RightStaminaBar/styles/background = SubResource("StyleBoxFlat_aliwb") +RightStaminaBar/styles/fill = SubResource("StyleBoxFlat_tnl2j") +StaminaBar/base_type = &"ProgressBar" +StaminaBar/styles/background = SubResource("StyleBoxFlat_k5faf") +StaminaBar/styles/fill = SubResource("StyleBoxFlat_5j0a2") +TabContainer/styles/panel = SubResource("StyleBoxFlat_odiue") +TabContainer/styles/tab_hovered = SubResource("StyleBoxFlat_ghjya") +TabContainer/styles/tab_selected = SubResource("StyleBoxFlat_m6y06") +TabContainer/styles/tab_unselected = SubResource("StyleBoxFlat_5icga") diff --git a/menus/resources/themes/lore.tres b/menus/resources/themes/lore.tres new file mode 100644 index 0000000..53dda34 --- /dev/null +++ b/menus/resources/themes/lore.tres @@ -0,0 +1,190 @@ +[gd_resource type="Theme" format=3 uid="uid://cpdklrs7didjo"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_8hxc2"] +content_margin_left = 8.0 +content_margin_top = 8.0 +content_margin_right = 8.0 +content_margin_bottom = 8.0 +bg_color = Color(0.631373, 0.52549, 0.619608, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.192157, 0.239216, 0.352941, 1) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_daw1f"] +content_margin_left = 8.0 +content_margin_top = 8.0 +content_margin_right = 8.0 +content_margin_bottom = 8.0 +bg_color = Color(0.00392157, 0.0862745, 0.152941, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.839216, 0.933333, 1, 1) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_r1yu6"] +content_margin_left = 8.0 +content_margin_top = 8.0 +content_margin_right = 8.0 +content_margin_bottom = 8.0 +bg_color = Color(0.00392157, 0.0862745, 0.152941, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.00392157, 0.0862745, 0.152941, 1) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_wnp2l"] +content_margin_left = 8.0 +content_margin_top = 8.0 +content_margin_right = 8.0 +content_margin_bottom = 8.0 +bg_color = Color(0.192157, 0.239216, 0.352941, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.00392157, 0.0862745, 0.152941, 1) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_2ymfe"] +bg_color = Color(0.00392157, 0.0862745, 0.152941, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.8, 0.8, 0.8, 0) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_pq3iw"] +bg_color = Color(0.00392157, 0.0862745, 0.152941, 1) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_hvab5"] +bg_color = Color(0.192157, 0.239216, 0.352941, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.00392157, 0.0862745, 0.152941, 1) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_f05by"] +bg_color = Color(0.192157, 0.239216, 0.352941, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.00392157, 0.0862745, 0.152941, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_t1x62"] +bg_color = Color(0.929412, 0.921569, 0.627451, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.8, 0.8, 0.8, 0) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_w2bse"] +content_margin_left = 8.0 +content_margin_top = 8.0 +content_margin_right = 8.0 +content_margin_bottom = 4.0 +bg_color = Color(0.00392157, 0.0862745, 0.152941, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_color = Color(0.839216, 0.933333, 1, 1) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_7eahf"] +content_margin_left = 8.0 +content_margin_top = 8.0 +content_margin_right = 8.0 +content_margin_bottom = 4.0 +bg_color = Color(0.192157, 0.239216, 0.352941, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_color = Color(0.00392157, 0.0862745, 0.152941, 1) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_5sk2t"] +content_margin_left = 8.0 +content_margin_top = 8.0 +content_margin_right = 8.0 +content_margin_bottom = 4.0 +bg_color = Color(0.00392157, 0.0862745, 0.152941, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_color = Color(0.00392157, 0.0862745, 0.152941, 1) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_00w47"] +bg_color = Color(0.839216, 0.933333, 1, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.8, 0.8, 0.8, 0) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_gh53c"] +bg_color = Color(0.631373, 0.52549, 0.619608, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.8, 0.8, 0.8, 0) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[resource] +Button/colors/font_color = Color(0.839216, 0.933333, 1, 1) +Button/colors/font_disabled_color = Color(0.192157, 0.239216, 0.352941, 1) +Button/colors/font_focus_color = Color(0.839216, 0.933333, 1, 1) +Button/colors/font_hover_color = Color(0.839216, 0.933333, 1, 1) +Button/colors/font_hover_pressed_color = Color(0.839216, 0.933333, 1, 1) +Button/colors/font_pressed_color = Color(0.00392157, 0.0862745, 0.152941, 1) +Button/styles/disabled = SubResource("StyleBoxFlat_8hxc2") +Button/styles/hover = SubResource("StyleBoxFlat_daw1f") +Button/styles/normal = SubResource("StyleBoxFlat_r1yu6") +Button/styles/pressed = SubResource("StyleBoxFlat_wnp2l") +DelayProgressBar/base_type = &"ProgressBar" +DelayProgressBar/styles/fill = SubResource("StyleBoxFlat_2ymfe") +Label/colors/font_color = Color(0.839216, 0.933333, 1, 1) +Label/colors/font_outline_color = Color(0.00392157, 0.0862745, 0.152941, 1) +Label/constants/outline_size = 8 +LineEdit/colors/caret_color = Color(0.839216, 0.933333, 1, 1) +LineEdit/colors/font_color = Color(0.929412, 0.921569, 0.627451, 1) +LineEdit/colors/font_uneditable_color = Color(0.192157, 0.239216, 0.352941, 1) +LineEdit/styles/normal = SubResource("StyleBoxFlat_pq3iw") +LineEdit/styles/read_only = SubResource("StyleBoxFlat_pq3iw") +Panel/styles/panel = SubResource("StyleBoxFlat_hvab5") +PanelContainer/styles/panel = SubResource("StyleBoxFlat_hvab5") +ProgressBar/styles/background = SubResource("StyleBoxFlat_f05by") +ProgressBar/styles/fill = SubResource("StyleBoxFlat_t1x62") +RichTextLabel/colors/default_color = Color(0.839216, 0.933333, 1, 1) +RichTextLabel/colors/font_outline_color = Color(0.00392157, 0.0862745, 0.152941, 1) +RichTextLabel/constants/outline_size = 8 +TabContainer/styles/panel = SubResource("StyleBoxFlat_hvab5") +TabContainer/styles/tab_hovered = SubResource("StyleBoxFlat_w2bse") +TabContainer/styles/tab_selected = SubResource("StyleBoxFlat_7eahf") +TabContainer/styles/tab_unselected = SubResource("StyleBoxFlat_5sk2t") +TimerProgressBar/base_type = &"ProgressBar" +TimerProgressBar/styles/fill = SubResource("StyleBoxFlat_00w47") +WarningProgressBar/base_type = &"ProgressBar" +WarningProgressBar/styles/fill = SubResource("StyleBoxFlat_gh53c") diff --git a/menus/resources/themes/steal_this_theme.tres b/menus/resources/themes/steal_this_theme.tres new file mode 100644 index 0000000..f27855e --- /dev/null +++ b/menus/resources/themes/steal_this_theme.tres @@ -0,0 +1,560 @@ +[gd_resource type="Theme" format=3 uid="uid://dimfu60mtxyar"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_7rtxy"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0, 0, 0, 1) +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color(0.698039, 0.133333, 0.203922, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_hofdy"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0, 0, 0, 0) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.235294, 0.231373, 0.431373, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 +expand_margin_left = 2.0 +expand_margin_top = 2.0 +expand_margin_right = 2.0 +expand_margin_bottom = 2.0 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_0ahyh"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0, 0, 0, 1) +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color(0.973535, 0.973535, 0.973535, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_83bj2"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.973535, 0.973535, 0.973535, 1) +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color(0, 0, 0, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_bjb6u"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0, 0, 0, 1) +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color(0.235294, 0.231373, 0.431373, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_wv8md"] +bg_color = Color(0.698039, 0.133333, 0.203922, 1) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_s1fdf"] +bg_color = Color(0.973535, 0.973535, 0.973535, 1) +corner_radius_top_left = 2 +corner_radius_top_right = 2 +corner_radius_bottom_right = 2 +corner_radius_bottom_left = 2 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_tieq2"] +bg_color = Color(0, 0, 0, 1) +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color(0.973535, 0.973535, 0.973535, 1) +corner_radius_top_left = 2 +corner_radius_top_right = 2 +corner_radius_bottom_right = 2 +corner_radius_bottom_left = 2 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_rrxf3"] +bg_color = Color(0, 0, 0, 1) +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color(0.235294, 0.231373, 0.431373, 1) +corner_radius_top_left = 2 +corner_radius_top_right = 2 +corner_radius_bottom_right = 2 +corner_radius_bottom_left = 2 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_2e0dr"] +content_margin_top = 3.0 +content_margin_bottom = 3.0 +bg_color = Color(0.0392157, 0.0392157, 0.0392157, 1) +corner_radius_top_left = 2 +corner_radius_top_right = 2 +corner_radius_bottom_right = 2 +corner_radius_bottom_left = 2 + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_fgisk"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_fgisk"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0, 0, 0, 1) +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color(0.973535, 0.973535, 0.973535, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_5e2ta"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.0383972, 0.0383972, 0.0383972, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_7e08u"] +bg_color = Color(0, 0, 0, 1) +border_color = Color(0.973535, 0.973535, 0.973535, 1) +corner_radius_top_left = 2 +corner_radius_top_right = 2 +corner_radius_bottom_right = 2 +corner_radius_bottom_left = 2 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_whago"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.235294, 0.231373, 0.431373, 1) +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color(0, 0, 0, 1) +corner_radius_top_left = 2 +corner_radius_top_right = 2 +corner_radius_bottom_right = 2 +corner_radius_bottom_left = 2 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_d8x3d"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.698039, 0.133333, 0.203922, 1) +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color(0, 0, 0, 1) +corner_radius_top_left = 2 +corner_radius_top_right = 2 +corner_radius_bottom_right = 2 +corner_radius_bottom_left = 2 + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_hofdy"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_lmfyq"] +bg_color = Color(0.698039, 0.133333, 0.203922, 1) +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color(0, 0, 0, 0) +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_wv8md"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_c4ulf"] +bg_color = Color(0, 0, 0, 1) +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color(0, 0, 0, 0) +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_8723n"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.235294, 0.231373, 0.431373, 1) +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color(0, 0, 0, 1) +corner_radius_top_left = 2 +corner_radius_top_right = 2 +corner_radius_bottom_right = 2 +corner_radius_bottom_left = 2 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_opsya"] +bg_color = Color(0.235294, 0.231373, 0.431373, 1) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_8g14u"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.0383972, 0.0383972, 0.0383972, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_qnvbk"] +content_margin_left = 8.0 +content_margin_top = 4.0 +content_margin_right = 8.0 +content_margin_bottom = 4.0 +bg_color = Color(0, 0, 0, 1) +border_width_top = 2 +border_color = Color(0.698039, 0.133333, 0.203922, 1) +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_d8x3d"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_4bfjk"] +content_margin_left = 8.0 +content_margin_top = 4.0 +content_margin_right = 8.0 +content_margin_bottom = 4.0 +bg_color = Color(0, 0, 0, 1) +border_width_top = 2 +border_color = Color(0.235294, 0.231373, 0.431373, 1) +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ojvr3"] +content_margin_left = 8.0 +content_margin_top = 4.0 +content_margin_right = 8.0 +content_margin_bottom = 4.0 +bg_color = Color(0.0392157, 0.0392157, 0.0392157, 1) +border_width_top = 2 +border_color = Color(0.973535, 0.973535, 0.973535, 1) +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_lop2v"] +content_margin_left = 8.0 +content_margin_top = 4.0 +content_margin_right = 8.0 +content_margin_bottom = 4.0 +bg_color = Color(0, 0, 0, 1) +border_width_top = 2 +border_color = Color(0.0392157, 0.0392157, 0.0392157, 1) +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_0ahyh"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_83bj2"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_rr4b1"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0, 0, 0, 0) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.235294, 0.231373, 0.431373, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 +expand_margin_left = 2.0 +expand_margin_top = 2.0 +expand_margin_right = 2.0 +expand_margin_bottom = 2.0 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_bcw1c"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.0383972, 0.0383972, 0.0383972, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_xi1kj"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0, 0, 0, 1) +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color(0.973535, 0.973535, 0.973535, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_rrcvo"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.235294, 0.231373, 0.431373, 1) +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color(0.973535, 0.973535, 0.973535, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_wvge0"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.235294, 0.231373, 0.431373, 1) +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color(0.973535, 0.973535, 0.973535, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_v0ggg"] +bg_color = Color(0.973535, 0.973535, 0.973535, 1) +corner_radius_top_left = 2 +corner_radius_top_right = 2 +corner_radius_bottom_right = 2 +corner_radius_bottom_left = 2 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_kvvmu"] +bg_color = Color(0, 0, 0, 1) +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color(0.973535, 0.973535, 0.973535, 1) +corner_radius_top_left = 2 +corner_radius_top_right = 2 +corner_radius_bottom_right = 2 +corner_radius_bottom_left = 2 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_rum38"] +bg_color = Color(0, 0, 0, 1) +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color(0.235294, 0.231373, 0.431373, 1) +corner_radius_top_left = 2 +corner_radius_top_right = 2 +corner_radius_bottom_right = 2 +corner_radius_bottom_left = 2 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_5wbgk"] +content_margin_left = 3.0 +content_margin_right = 3.0 +bg_color = Color(0.0392157, 0.0392157, 0.0392157, 1) +corner_radius_top_left = 2 +corner_radius_top_right = 2 +corner_radius_bottom_right = 2 +corner_radius_bottom_left = 2 + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_s1fdf"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_x127s"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.698039, 0.133333, 0.203922, 1) +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color(0, 0, 0, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_qjhx0"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0, 0, 0, 1) +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color(0.698039, 0.133333, 0.203922, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_u427p"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0, 0, 0, 1) +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color(0.698039, 0.133333, 0.203922, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[resource] +BoldLabel/base_type = &"Label" +BoldLabel/fonts/font = null +Button/colors/font_color = Color(0, 0, 0, 1) +Button/colors/font_disabled_color = Color(0.698039, 0.133333, 0.203922, 1) +Button/colors/font_focus_color = Color(0, 0, 0, 1) +Button/colors/font_hover_color = Color(0.973535, 0.973535, 0.973535, 1) +Button/colors/font_hover_pressed_color = Color(0.973535, 0.973535, 0.973535, 1) +Button/colors/font_pressed_color = Color(0.235294, 0.231373, 0.431373, 1) +Button/styles/disabled = SubResource("StyleBoxFlat_7rtxy") +Button/styles/focus = SubResource("StyleBoxFlat_hofdy") +Button/styles/hover = SubResource("StyleBoxFlat_0ahyh") +Button/styles/normal = SubResource("StyleBoxFlat_83bj2") +Button/styles/pressed = SubResource("StyleBoxFlat_bjb6u") +DisabledLabel/base_type = &"Label" +DisabledLabel/colors/font_color = Color(0, 0, 0, 1) +DisabledLabel/font_sizes/font_size = 24 +DisabledLabel/fonts/font = null +DisabledLabel/styles/normal = SubResource("StyleBoxFlat_wv8md") +HScrollBar/styles/grabber = SubResource("StyleBoxFlat_s1fdf") +HScrollBar/styles/grabber_highlight = SubResource("StyleBoxFlat_tieq2") +HScrollBar/styles/grabber_pressed = SubResource("StyleBoxFlat_rrxf3") +HScrollBar/styles/scroll = SubResource("StyleBoxFlat_2e0dr") +HScrollBar/styles/scroll_focus = SubResource("StyleBoxEmpty_fgisk") +LocationName/base_type = &"Label" +LocationName/colors/font_color = Color(0.973535, 0.973535, 0.973535, 1) +LocationName/font_sizes/font_size = 22 +LocationName/fonts/font = null +LocationType/base_type = &"Label" +LocationType/colors/font_color = Color(0.235294, 0.231373, 0.431373, 1) +LocationType/fonts/font = null +Panel/styles/panel = SubResource("StyleBoxFlat_fgisk") +PanelContainer/styles/panel = SubResource("StyleBoxFlat_5e2ta") +ProgressBar/styles/background = SubResource("StyleBoxFlat_7e08u") +ProgressBar/styles/fill = SubResource("StyleBoxFlat_whago") +ProgressBarBad/base_type = &"ProgressBar" +ProgressBarBad/styles/fill = SubResource("StyleBoxFlat_d8x3d") +ProgressBarDelay/base_type = &"ProgressBar" +ProgressBarDelay/styles/background = SubResource("StyleBoxEmpty_hofdy") +ProgressBarDelay/styles/fill = SubResource("StyleBoxFlat_lmfyq") +ProgressBarDelayHovered/base_type = &"ProgressBar" +ProgressBarDelayHovered/styles/background = SubResource("StyleBoxEmpty_wv8md") +ProgressBarDelayHovered/styles/fill = SubResource("StyleBoxFlat_c4ulf") +ProgressBarGood/base_type = &"ProgressBar" +ProgressBarGood/styles/fill = SubResource("StyleBoxFlat_8723n") +RichTextLabel/fonts/bold_font = null +RichTextLabel/fonts/bold_italics_font = null +RichTextLabel/fonts/italics_font = null +SelectedLabel/base_type = &"Label" +SelectedLabel/colors/font_color = Color(0.973535, 0.973535, 0.973535, 1) +SelectedLabel/font_sizes/font_size = 24 +SelectedLabel/fonts/font = null +SelectedLabel/styles/normal = SubResource("StyleBoxFlat_opsya") +TabContainer/colors/font_disabled_color = Color(0.698039, 0.133333, 0.203922, 1) +TabContainer/colors/font_hovered_color = Color(0.235294, 0.231373, 0.431373, 1) +TabContainer/colors/font_selected_color = Color(0.973535, 0.973535, 0.973535, 1) +TabContainer/colors/font_unselected_color = Color(0.973535, 0.973535, 0.973535, 1) +TabContainer/styles/panel = SubResource("StyleBoxFlat_8g14u") +TabContainer/styles/tab_disabled = SubResource("StyleBoxFlat_qnvbk") +TabContainer/styles/tab_focus = SubResource("StyleBoxEmpty_d8x3d") +TabContainer/styles/tab_hovered = SubResource("StyleBoxFlat_4bfjk") +TabContainer/styles/tab_selected = SubResource("StyleBoxFlat_ojvr3") +TabContainer/styles/tab_unselected = SubResource("StyleBoxFlat_lop2v") +Tree/colors/children_hl_line_color = Color(0.0392157, 0.0392157, 0.0392157, 1) +Tree/colors/font_color = Color(0.973535, 0.973535, 0.973535, 1) +Tree/colors/font_disabled_color = Color(0.698039, 0.133333, 0.203922, 1) +Tree/colors/font_hovered_color = Color(0.235294, 0.231373, 0.431373, 1) +Tree/colors/font_selected_color = Color(0.973535, 0.973535, 0.973535, 1) +Tree/colors/guide_color = Color(0, 0, 0, 0) +Tree/colors/parent_hl_line_color = Color(0.0392157, 0.0392157, 0.0392157, 1) +Tree/colors/relationship_line_color = Color(0.0392157, 0.0392157, 0.0392157, 1) +Tree/constants/inner_item_margin_left = 4 +Tree/constants/inner_item_margin_right = 4 +Tree/constants/item_margin = 0 +Tree/styles/button_hover = SubResource("StyleBoxEmpty_0ahyh") +Tree/styles/button_pressed = SubResource("StyleBoxEmpty_83bj2") +Tree/styles/focus = SubResource("StyleBoxFlat_rr4b1") +Tree/styles/hovered = SubResource("StyleBoxFlat_bcw1c") +Tree/styles/panel = SubResource("StyleBoxFlat_xi1kj") +Tree/styles/selected = SubResource("StyleBoxFlat_rrcvo") +Tree/styles/selected_focus = SubResource("StyleBoxFlat_wvge0") +VScrollBar/styles/grabber = SubResource("StyleBoxFlat_v0ggg") +VScrollBar/styles/grabber_highlight = SubResource("StyleBoxFlat_kvvmu") +VScrollBar/styles/grabber_pressed = SubResource("StyleBoxFlat_rum38") +VScrollBar/styles/scroll = SubResource("StyleBoxFlat_5wbgk") +VScrollBar/styles/scroll_focus = SubResource("StyleBoxEmpty_s1fdf") +WaitingButton/base_type = &"Button" +WaitingButton/colors/font_color = Color(0.698039, 0.133333, 0.203922, 1) +WaitingButton/colors/font_focus_color = Color(0.698039, 0.133333, 0.203922, 1) +WaitingButton/colors/font_hover_color = Color(0, 0, 0, 1) +WaitingButton/colors/font_hover_pressed_color = Color(0.698039, 0.133333, 0.203922, 1) +WaitingButton/colors/font_pressed_color = Color(0.698039, 0.133333, 0.203922, 1) +WaitingButton/styles/hover = SubResource("StyleBoxFlat_x127s") +WaitingButton/styles/normal = SubResource("StyleBoxFlat_qjhx0") +WaitingButton/styles/pressed = SubResource("StyleBoxFlat_u427p") diff --git a/menus/scenes/credits/credits_label.tscn b/menus/scenes/credits/credits_label.tscn new file mode 100644 index 0000000..65023fb --- /dev/null +++ b/menus/scenes/credits/credits_label.tscn @@ -0,0 +1,56 @@ +[gd_scene format=3 uid="uid://coug8dvtetdu6"] + +[ext_resource type="Script" path="res://addons/maaacks_game_template/base/nodes/labels/credits_label.gd" id="1_ao6uf"] + +[node name="CreditsLabel" type="RichTextLabel"] +custom_minimum_size = Vector2(1280, 0) +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +size_flags_vertical = 5 +bbcode_enabled = true +text = "[font_size=48]Collaborators[/font_size] + +[font_size=32]Role[/font_size] +Person 1 +Person 2 +[url=]Person w/ Link[/url] + +[font_size=48]Sourced[/font_size] +[font_size=32]Asset Type[/font_size] +[font_size=24]Use Case[/font_size] +Author: [url=]Name[/url] +Source: [url=]Domain : webpage.html[/url] +License: [url=]License[/url] + +[font_size=24]Godot Engine Logo[/font_size] +Author: Andrea Calabró +Source: [url=https://godotengine.org/press/]godotengine.org : press[/url] +License: [url=https://github.com/godotengine/godot/blob/master/LOGO_LICENSE.txt]CC BY 4.0 International[/url] + +[font_size=48]Tools[/font_size] +[font_size=24]Godot[/font_size] +[img=80]res:///menus/assets/godot_engine_logo/logo_vertical_color_dark.png[/img] +Author: [url=https://godotengine.org/contact]Juan Linietsky, Ariel Manzur, and contributors[/url] +Source: [url=https://godotengine.org/]godotengine.org[/url] +License: [url=https://github.com/godotengine/godot/blob/master/LICENSE.txt]MIT License[/url] + +[font_size=24]Godot Minimal Game Template[/font_size] +[img=80]res:///menus/assets/plugin_logo/logo.png[/img] +Author: [url=https://github.com/Maaack/Godot-Minimal-Game-Template/graphs/contributors]Marek Belski and contributors[/url] +Source: [url=https://github.com/Maaack/Godot-Minimal-Game-Template]github: Godot-Minimal-Game-Template[/url] +License: [url=LICENSE.txt]MIT License[/url] + +[font_size=24]Git[/font_size] +[img=80]res:///menus/assets/git_logo/Git-Logo-2Color.png[/img] +Author: [url=https://github.com/torvalds]Linus Torvalds[/url] +Source: [url=https://git-scm.com/downloads]git-scm.com[/url] +License: [url=https://opensource.org/licenses/GPL-2.0]GNU General Public License version 2[/url] +" +fit_content = true +scroll_active = false +horizontal_alignment = 1 +script = ExtResource("1_ao6uf") diff --git a/menus/scenes/credits/scrollable_credits.gd b/menus/scenes/credits/scrollable_credits.gd new file mode 100644 index 0000000..6de019f --- /dev/null +++ b/menus/scenes/credits/scrollable_credits.gd @@ -0,0 +1,29 @@ +@tool +extends Control + +@onready var credits_label : RichTextLabel = %CreditsLabel + +@export var input_scroll_speed : float = 10.0 + +var _line_number : float = 0 + +func _on_visibility_changed() -> void: + if visible: + credits_label.scroll_to_line(0) + credits_label.grab_focus() + +func _ready() -> void: + visibility_changed.connect(_on_visibility_changed) + +func _process(delta : float) -> void: + if Engine.is_editor_hint() or not visible: + return + var input_axis = Input.get_axis("ui_up", "ui_down") + if abs(input_axis) > 0.5: + _line_number += input_axis * delta * input_scroll_speed + var max_lines = credits_label.get_line_count() - credits_label.get_visible_line_count() + if _line_number < 0: + _line_number = 0 + if _line_number > max_lines: + _line_number = max_lines + credits_label.scroll_to_line(round(_line_number)) diff --git a/menus/scenes/credits/scrollable_credits.gd.uid b/menus/scenes/credits/scrollable_credits.gd.uid new file mode 100644 index 0000000..e7ddf18 --- /dev/null +++ b/menus/scenes/credits/scrollable_credits.gd.uid @@ -0,0 +1 @@ +uid://dyf426wsn270l diff --git a/menus/scenes/credits/scrollable_credits.tscn b/menus/scenes/credits/scrollable_credits.tscn new file mode 100644 index 0000000..61535af --- /dev/null +++ b/menus/scenes/credits/scrollable_credits.tscn @@ -0,0 +1,19 @@ +[gd_scene format=3 uid="uid://1y3twyudd5l8"] + +[ext_resource type="Script" path="res://menus/scenes/credits/scrollable_credits.gd" id="1_gcb84"] +[ext_resource type="PackedScene" path="res://menus/scenes/credits/credits_label.tscn" id="2_nuv7p"] + +[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_gcb84") + +[node name="CreditsLabel" parent="." instance=ExtResource("2_nuv7p")] +unique_name_in_owner = true +layout_mode = 1 +fit_content = false +scroll_active = true diff --git a/menus/scenes/credits/scrolling_credits.gd b/menus/scenes/credits/scrolling_credits.gd new file mode 100644 index 0000000..78ead19 --- /dev/null +++ b/menus/scenes/credits/scrolling_credits.gd @@ -0,0 +1,92 @@ +@tool +extends Control + +signal end_reached + +@export var auto_scroll_speed: float = 60.0 +@export var input_scroll_speed : float = 400.0 +@export var scroll_restart_delay : float = 1.5 +@export var scroll_paused : bool = false + +var timer : Timer = Timer.new() +var _current_scroll_position : float = 0.0 + +@onready var header_space : Control = %HeaderSpace +@onready var footer_space : Control = %FooterSpace +@onready var credits_label : Control = %CreditsLabel +@onready var scroll_container : ScrollContainer = %ScrollContainer + +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_resized() -> void: + set_header_and_footer() + _current_scroll_position = scroll_container.scroll_vertical + +func _end_reached() -> void: + scroll_paused = true + end_reached.emit() + +func is_end_reached() -> bool: + var _end_of_credits_vertical = credits_label.size.y + header_space.size.y + return scroll_container.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_container.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_container.scroll_vertical + scroll_paused = false + +func _on_visibility_changed() -> void: + if visible: + scroll_container.scroll_vertical = 0 + _current_scroll_position = scroll_container.scroll_vertical + scroll_paused = false + +func _ready() -> void: + scroll_container.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) + set_header_and_footer() + add_child(timer) + scroll_paused = false + + +func _process(delta : float) -> void: + var input_axis = Input.get_axis("ui_up", "ui_down") + if input_axis != 0: + _scroll_container(input_axis * input_scroll_speed * delta) + else: + _scroll_container(auto_scroll_speed * delta) + +func _exit_tree() -> void: + _current_scroll_position = scroll_container.scroll_vertical diff --git a/menus/scenes/credits/scrolling_credits.gd.uid b/menus/scenes/credits/scrolling_credits.gd.uid new file mode 100644 index 0000000..07cc39b --- /dev/null +++ b/menus/scenes/credits/scrolling_credits.gd.uid @@ -0,0 +1 @@ +uid://c1ac47rxkskux diff --git a/menus/scenes/credits/scrolling_credits.tscn b/menus/scenes/credits/scrolling_credits.tscn new file mode 100644 index 0000000..a176685 --- /dev/null +++ b/menus/scenes/credits/scrolling_credits.tscn @@ -0,0 +1,40 @@ +[gd_scene format=3 uid="uid://c6r0n1kinvqj7"] + +[ext_resource type="Script" path="res://menus/scenes/credits/scrolling_credits.gd" id="1_hy305"] +[ext_resource type="PackedScene" path="res://menus/scenes/credits/credits_label.tscn" id="2_ymsg3"] + +[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("1_hy305") + +[node name="ScrollContainer" type="ScrollContainer" parent="."] +unique_name_in_owner = true +layout_mode = 0 +anchor_right = 1.0 +anchor_bottom = 1.0 +scroll_vertical = 0 +horizontal_scroll_mode = 0 +vertical_scroll_mode = 3 + +[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" parent="ScrollContainer/VBoxContainer" instance=ExtResource("2_ymsg3")] +unique_name_in_owner = true +layout_mode = 2 + +[node name="FooterSpace" type="Control" parent="ScrollContainer/VBoxContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(0, 720) +layout_mode = 2 diff --git a/menus/scenes/end_credits/end_credits.gd b/menus/scenes/end_credits/end_credits.gd new file mode 100644 index 0000000..a790221 --- /dev/null +++ b/menus/scenes/end_credits/end_credits.gd @@ -0,0 +1,63 @@ +@tool +extends "res://menus/scenes/credits/scrolling_credits.gd" + +## Defines the path to the main menu. Hides the Main Menu button if not set. +@export_file("*.tscn") var main_menu_scene_path : String +## This option forces the mouse to be visible when the menu shows up. +## Useful for games that capture the mouse, and don't automatically return it. +@export var force_mouse_mode_visible : bool = false + +@onready var end_message_panel = %EndMessagePanel +@onready var exit_button = %ExitButton +@onready var menu_button = %MenuButton +@onready var init_mouse_filter : MouseFilter = mouse_filter +## If Maaack's Scene Loader is installed, then it will be used to change scenes. +@onready var scene_loader_node = get_tree().root.get_node_or_null(^"SceneLoader") + +func get_main_menu_scene_path() -> String: + return main_menu_scene_path + +func _end_reached() -> void: + end_message_panel.show() + mouse_filter = Control.MOUSE_FILTER_STOP + if force_mouse_mode_visible: + Input.mouse_mode = Input.MOUSE_MODE_VISIBLE + super._end_reached() + +func load_main_menu() -> void: + if scene_loader_node: + scene_loader_node.load_scene(get_main_menu_scene_path()) + else: + get_tree().change_scene_to_file(get_main_menu_scene_path()) + +func exit_game() -> void: + if OS.has_feature("web"): + load_main_menu() + get_tree().quit() + +func _on_visibility_changed() -> void: + if visible: + end_message_panel.hide() + mouse_filter = init_mouse_filter + super._on_visibility_changed() + +func _ready() -> void: + if get_main_menu_scene_path().is_empty(): + menu_button.hide() + if OS.has_feature("web"): + exit_button.hide() + end_message_panel.hide() + super._ready() + +func _unhandled_input(event : InputEvent) -> void: + if event.is_action_released("ui_cancel"): + if not end_message_panel.visible: + _end_reached() + else: + exit_game() + +func _on_exit_button_pressed(): + exit_game() + +func _on_menu_button_pressed(): + load_main_menu() diff --git a/menus/scenes/end_credits/end_credits.gd.uid b/menus/scenes/end_credits/end_credits.gd.uid new file mode 100644 index 0000000..a00717e --- /dev/null +++ b/menus/scenes/end_credits/end_credits.gd.uid @@ -0,0 +1 @@ +uid://do6bw0acofct2 diff --git a/menus/scenes/end_credits/end_credits.tscn b/menus/scenes/end_credits/end_credits.tscn new file mode 100644 index 0000000..46647f7 --- /dev/null +++ b/menus/scenes/end_credits/end_credits.tscn @@ -0,0 +1,85 @@ +[gd_scene format=3 uid="uid://bn4qfscxapbnx"] + +[ext_resource type="PackedScene" path="res://menus/scenes/credits/scrolling_credits.tscn" id="1_eh07o"] +[ext_resource type="Script" path="res://menus/scenes/end_credits/end_credits.gd" id="2_5f0mc"] +[ext_resource type="Script" path="res://addons/maaacks_game_template/base/nodes/utilities/capture_focus.gd" id="3_ixi0r"] + +[node name="EndCredits" instance=ExtResource("1_eh07o")] +script = ExtResource("2_5f0mc") +main_menu_scene_path = "res://menus/scenes/menus/main_menu/main_menu_with_animations.tscn" +force_mouse_mode_visible = false + +[node name="BackgroundColor" type="ColorRect" parent="." index="0"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(0, 0, 0, 1) + +[node name="BackgroundTextureRect" type="TextureRect" parent="." index="1"] +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="CenterContainer" type="CenterContainer" parent="." index="3"] +layout_mode = 0 +anchor_right = 1.0 +anchor_bottom = 1.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +mouse_filter = 2 + +[node name="EndMessagePanel" type="Panel" parent="CenterContainer" index="0"] +unique_name_in_owner = true +visible = false +custom_minimum_size = Vector2(360, 120) +layout_mode = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer/EndMessagePanel" index="0"] +layout_mode = 0 +anchor_right = 1.0 +anchor_bottom = 1.0 + +[node name="ThankPlayer" type="Label" parent="CenterContainer/EndMessagePanel/VBoxContainer" index="0"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +text = "Thanks for playing!" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="CenterContainer" type="CenterContainer" parent="CenterContainer/EndMessagePanel/VBoxContainer" index="1"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="HBoxContainer" type="HBoxContainer" parent="CenterContainer/EndMessagePanel/VBoxContainer/CenterContainer" index="0"] +custom_minimum_size = Vector2(256, 0) +layout_mode = 2 +size_flags_vertical = 3 +theme_override_constants/separation = 16 +script = ExtResource("3_ixi0r") + +[node name="ExitButton" type="Button" parent="CenterContainer/EndMessagePanel/VBoxContainer/CenterContainer/HBoxContainer" index="0"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +text = "Exit" + +[node name="MenuButton" type="Button" parent="CenterContainer/EndMessagePanel/VBoxContainer/CenterContainer/HBoxContainer" index="1"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +text = "Menu" + +[connection signal="pressed" from="CenterContainer/EndMessagePanel/VBoxContainer/CenterContainer/HBoxContainer/ExitButton" to="." method="_on_exit_button_pressed"] +[connection signal="pressed" from="CenterContainer/EndMessagePanel/VBoxContainer/CenterContainer/HBoxContainer/MenuButton" to="." method="_on_menu_button_pressed"] diff --git a/menus/scenes/game_scene/configurable_sub_viewport.gd b/menus/scenes/game_scene/configurable_sub_viewport.gd new file mode 100644 index 0000000..105e9f6 --- /dev/null +++ b/menus/scenes/game_scene/configurable_sub_viewport.gd @@ -0,0 +1,12 @@ +extends SubViewport +## Script to apply the anti-aliasing setting from [PlayerConfig] to a [SubViewport]. + +## The name of the anti-aliasing variable in the [ConfigFile]. +@export var anti_aliasing_key : StringName = "Anti-aliasing" +## The name of the section of the anti-aliasing variable in the [ConfigFile]. +@export var video_section : StringName = AppSettings.VIDEO_SECTION + +func _ready() -> void: + var anti_aliasing : int = PlayerConfig.get_config(video_section, anti_aliasing_key, Viewport.MSAA_DISABLED) + msaa_2d = anti_aliasing as MSAA + msaa_3d = anti_aliasing as MSAA diff --git a/menus/scenes/game_scene/configurable_sub_viewport.gd.uid b/menus/scenes/game_scene/configurable_sub_viewport.gd.uid new file mode 100644 index 0000000..bd178f0 --- /dev/null +++ b/menus/scenes/game_scene/configurable_sub_viewport.gd.uid @@ -0,0 +1 @@ +uid://sriramsx75sj diff --git a/menus/scenes/game_scene/game_ui.tscn b/menus/scenes/game_scene/game_ui.tscn new file mode 100644 index 0000000..e3272fc --- /dev/null +++ b/menus/scenes/game_scene/game_ui.tscn @@ -0,0 +1,61 @@ +[gd_scene format=3 uid="uid://c3cpdpo8fcass"] + +[ext_resource type="Script" path="res://addons/maaacks_game_template/base/nodes/utilities/pause_menu_controller.gd" id="1_78lb1"] +[ext_resource type="PackedScene" path="res://menus/scenes/windows/pause_menu_layer.tscn" id="2_fc12x"] +[ext_resource type="Script" path="res://addons/maaacks_game_template/extras/scripts/level_loader.gd" id="3_xinp6"] +[ext_resource type="Script" path="res://addons/maaacks_game_template/extras/scripts/level_manager.gd" id="4_8vrg7"] +[ext_resource type="PackedScene" path="res://menus/scenes/windows/game_won_window.tscn" id="5_5qwaf"] +[ext_resource type="PackedScene" path="res://menus/scenes/windows/level_lost_window.tscn" id="6_0tw8m"] +[ext_resource type="PackedScene" path="res://menus/scenes/windows/level_won_window.tscn" id="7_gpclb"] +[ext_resource type="Script" path="res://addons/maaacks_game_template/extras/scripts/scene_lister.gd" id="8_yl32o"] +[ext_resource type="Script" path="res://menus/scenes/game_scene/configurable_sub_viewport.gd" id="9_iioxl"] + +[node name="GameUI" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="PauseMenuController" type="Node" parent="." node_paths=PackedStringArray("focused_viewport")] +script = ExtResource("1_78lb1") +pause_menu_packed = ExtResource("2_fc12x") +focused_viewport = NodePath("../ViewportContainer/ConfigurableSubViewport") + +[node name="LevelLoader" type="Node" parent="." node_paths=PackedStringArray("level_container")] +script = ExtResource("3_xinp6") +level_container = NodePath("../ViewportContainer/ConfigurableSubViewport") + +[node name="LevelManager" type="Node" parent="." node_paths=PackedStringArray("level_loader", "scene_lister")] +script = ExtResource("4_8vrg7") +level_loader = NodePath("../LevelLoader") +starting_level_path = "res://menus/scenes/game_scene/levels/level_1.tscn" +scene_lister = NodePath("SceneLister") +main_menu_scene_path = "res://menus/scenes/menus/main_menu/main_menu_with_animations.tscn" +ending_scene_path = "res://menus/scenes/end_credits/end_credits.tscn" +game_won_scene = ExtResource("5_5qwaf") +level_lost_scene = ExtResource("6_0tw8m") +level_won_scene = ExtResource("7_gpclb") + +[node name="SceneLister" type="Node" parent="LevelManager"] +script = ExtResource("8_yl32o") +files = Array[String](["res://menus/scenes/game_scene/levels/level_1.tscn", "res://menus/scenes/game_scene/levels/level_2.tscn", "res://menus/scenes/game_scene/levels/level_3.tscn"]) +directory = "res://menus/scenes/game_scene/levels" + +[node name="ViewportContainer" type="SubViewportContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +stretch = true + +[node name="ConfigurableSubViewport" type="SubViewport" parent="ViewportContainer"] +handle_input_locally = false +audio_listener_enable_2d = true +audio_listener_enable_3d = true +size = Vector2i(1280, 720) +render_target_update_mode = 4 +script = ExtResource("9_iioxl") diff --git a/menus/scenes/game_scene/input_display_label.gd b/menus/scenes/game_scene/input_display_label.gd new file mode 100644 index 0000000..a57c4d2 --- /dev/null +++ b/menus/scenes/game_scene/input_display_label.gd @@ -0,0 +1,21 @@ +extends Label + +@onready var action_names := AppSettings.get_action_names() + +func _get_inputs_as_string() -> String: + var all_inputs : String = "" + var is_first : bool = true + for action_name in action_names: + if Input.is_action_pressed(action_name): + if is_first: + is_first = false + all_inputs += action_name + else: + all_inputs += " + " + action_name + return all_inputs + +func _process(_delta : float) -> void: + if Input.is_anything_pressed(): + text = _get_inputs_as_string() + else: + text = "" diff --git a/menus/scenes/game_scene/input_display_label.gd.uid b/menus/scenes/game_scene/input_display_label.gd.uid new file mode 100644 index 0000000..24cb2ca --- /dev/null +++ b/menus/scenes/game_scene/input_display_label.gd.uid @@ -0,0 +1 @@ +uid://dno0fo2wvo8nn diff --git a/menus/scenes/game_scene/levels/level.gd b/menus/scenes/game_scene/levels/level.gd new file mode 100644 index 0000000..b0fa9c3 --- /dev/null +++ b/menus/scenes/game_scene/levels/level.gd @@ -0,0 +1,24 @@ +extends Node + +signal level_lost +signal level_won(level_path : String) +@warning_ignore("unused_signal") +signal level_changed(level_path : String) + +## Optional path to the next level if using an open world level system. +@export_file("*.tscn") var next_level_path : String + +func _on_lose_button_pressed() -> void: + level_lost.emit() + +func _on_win_button_pressed() -> void: + level_won.emit(next_level_path) + +func open_tutorials() -> void: + %TutorialManager.open_tutorials() + +func _ready() -> void: + open_tutorials() + +func _on_tutorial_button_pressed() -> void: + open_tutorials() diff --git a/menus/scenes/game_scene/levels/level.gd.uid b/menus/scenes/game_scene/levels/level.gd.uid new file mode 100644 index 0000000..cf05387 --- /dev/null +++ b/menus/scenes/game_scene/levels/level.gd.uid @@ -0,0 +1 @@ +uid://d3st2mmp4nlec diff --git a/menus/scenes/game_scene/levels/level_1.tscn b/menus/scenes/game_scene/levels/level_1.tscn new file mode 100644 index 0000000..da8f6ac --- /dev/null +++ b/menus/scenes/game_scene/levels/level_1.tscn @@ -0,0 +1,91 @@ +[gd_scene format=3 uid="uid://blhb80jdsu3j5"] + +[ext_resource type="Script" path="res://menus/scenes/game_scene/levels/level.gd" id="1_urxkg"] +[ext_resource type="Script" path="res://addons/maaacks_game_template/base/nodes/utilities/capture_focus.gd" id="2_7sa42"] +[ext_resource type="Script" path="res://menus/scenes/game_scene/input_display_label.gd" id="3_l3jem"] +[ext_resource type="Script" path="res://menus/scenes/game_scene/tutorial_manager.gd" id="4_rqjq6"] +[ext_resource type="PackedScene" path="res://menus/scenes/game_scene/tutorials/tutorial_1.tscn" id="5_n53h6"] + +[node name="Level1" 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_urxkg") +next_level_path = "res://menus/scenes/game_scene/levels/level_2.tscn" + +[node name="BackgroundColor" type="ColorRect" 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 +color = Color(0, 0, 0, 1) + +[node name="MarginContainer" 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 = 32 +theme_override_constants/margin_top = 32 +theme_override_constants/margin_right = 32 +theme_override_constants/margin_bottom = 32 + +[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] +layout_mode = 2 +theme_override_constants/separation = 16 + +[node name="Label" type="Label" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 +theme_override_font_sizes/font_size = 32 +text = "Example Level #1" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="HSeparator" type="HSeparator" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 +theme_override_constants/separation = 32 +script = ExtResource("2_7sa42") + +[node name="LoseButton" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Lose" + +[node name="WinButton" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Win" + +[node name="HBoxContainer2" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="TutorialButton" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer2"] +layout_mode = 2 +text = "Tutorial" + +[node name="InputDisplayLabel" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer2"] +layout_mode = 2 +size_flags_horizontal = 3 +horizontal_alignment = 1 +script = ExtResource("3_l3jem") + +[node name="TutorialManager" type="Node" parent="."] +unique_name_in_owner = true +script = ExtResource("4_rqjq6") +tutorial_scenes = Array[PackedScene]([ExtResource("5_n53h6")]) + +[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/LoseButton" to="." method="_on_lose_button_pressed"] +[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/WinButton" to="." method="_on_win_button_pressed"] +[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer2/TutorialButton" to="." method="_on_tutorial_button_pressed"] diff --git a/menus/scenes/game_scene/levels/level_2.tscn b/menus/scenes/game_scene/levels/level_2.tscn new file mode 100644 index 0000000..f2c49c4 --- /dev/null +++ b/menus/scenes/game_scene/levels/level_2.tscn @@ -0,0 +1,97 @@ +[gd_scene format=3 uid="uid://j11k4ykeskdk"] + +[ext_resource type="Script" path="res://menus/scenes/game_scene/levels/level.gd" id="1_dm1lp"] +[ext_resource type="Script" path="res://addons/maaacks_game_template/base/nodes/utilities/capture_focus.gd" id="2_xr7u2"] +[ext_resource type="Script" path="res://menus/scenes/game_scene/input_display_label.gd" id="3_30wab"] +[ext_resource type="Script" path="res://menus/scenes/game_scene/tutorial_manager.gd" id="4_wuxsx"] +[ext_resource type="PackedScene" path="res://menus/scenes/game_scene/tutorials/tutorial_2.tscn" id="5_bab38"] + +[node name="Level2" 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_dm1lp") +next_level_path = "res://menus/scenes/game_scene/levels/level_3.tscn" + +[node name="BackgroundColor" type="ColorRect" 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 +color = Color(0, 0, 0, 1) + +[node name="MarginContainer" 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 = 32 +theme_override_constants/margin_top = 32 +theme_override_constants/margin_right = 32 +theme_override_constants/margin_bottom = 32 + +[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] +layout_mode = 2 +theme_override_constants/separation = 16 + +[node name="Label" type="Label" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 +theme_override_font_sizes/font_size = 32 +text = "Example Level #2" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="HSeparator" type="HSeparator" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 +theme_override_constants/separation = 32 +script = ExtResource("2_xr7u2") + +[node name="LoseButton" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Lose" + +[node name="WinButton" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Win" + +[node name="LoseButton2" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Lose" + +[node name="HBoxContainer2" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="TutorialButton" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer2"] +layout_mode = 2 +text = "Tutorial" + +[node name="InputDisplayLabel" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer2"] +layout_mode = 2 +size_flags_horizontal = 3 +horizontal_alignment = 1 +script = ExtResource("3_30wab") + +[node name="TutorialManager" type="Node" parent="."] +unique_name_in_owner = true +script = ExtResource("4_wuxsx") +tutorial_scenes = Array[PackedScene]([ExtResource("5_bab38")]) + +[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/LoseButton" to="." method="_on_lose_button_pressed"] +[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/WinButton" to="." method="_on_win_button_pressed"] +[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/LoseButton2" to="." method="_on_lose_button_pressed"] +[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer2/TutorialButton" to="." method="_on_tutorial_button_pressed"] diff --git a/menus/scenes/game_scene/levels/level_3.tscn b/menus/scenes/game_scene/levels/level_3.tscn new file mode 100644 index 0000000..6f07820 --- /dev/null +++ b/menus/scenes/game_scene/levels/level_3.tscn @@ -0,0 +1,102 @@ +[gd_scene format=3 uid="uid://cbxhfrfjktqmp"] + +[ext_resource type="Script" path="res://menus/scenes/game_scene/levels/level.gd" id="1_ufhlx"] +[ext_resource type="Script" path="res://addons/maaacks_game_template/base/nodes/utilities/capture_focus.gd" id="2_rjwxn"] +[ext_resource type="Script" path="res://menus/scenes/game_scene/input_display_label.gd" id="3_m48ar"] +[ext_resource type="Script" path="res://menus/scenes/game_scene/tutorial_manager.gd" id="4_j0xup"] +[ext_resource type="PackedScene" path="res://menus/scenes/game_scene/tutorials/tutorial_3.tscn" id="5_hpu0q"] + +[node name="Level3" 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_ufhlx") + +[node name="BackgroundColor" type="ColorRect" 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 +color = Color(0, 0, 0, 1) + +[node name="MarginContainer" 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 = 32 +theme_override_constants/margin_top = 32 +theme_override_constants/margin_right = 32 +theme_override_constants/margin_bottom = 32 + +[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] +layout_mode = 2 +theme_override_constants/separation = 16 + +[node name="Label" type="Label" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 +theme_override_font_sizes/font_size = 32 +text = "Example Level #3" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="HSeparator" type="HSeparator" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 +theme_override_constants/separation = 32 +script = ExtResource("2_rjwxn") + +[node name="WinButton" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Win" + +[node name="LoseButton" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Lose" + +[node name="LoseButton2" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Lose" + +[node name="LoseButton3" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Lose" + +[node name="HBoxContainer2" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="TutorialButton" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer2"] +layout_mode = 2 +text = "Tutorial" + +[node name="InputDisplayLabel" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer2"] +layout_mode = 2 +size_flags_horizontal = 3 +horizontal_alignment = 1 +script = ExtResource("3_m48ar") + +[node name="TutorialManager" type="Node" parent="."] +unique_name_in_owner = true +script = ExtResource("4_j0xup") +tutorial_scenes = Array[PackedScene]([ExtResource("5_hpu0q")]) + +[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/WinButton" to="." method="_on_win_button_pressed"] +[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/LoseButton" to="." method="_on_lose_button_pressed"] +[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/LoseButton2" to="." method="_on_lose_button_pressed"] +[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/LoseButton3" to="." method="_on_lose_button_pressed"] +[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer2/TutorialButton" to="." method="_on_tutorial_button_pressed"] diff --git a/menus/scenes/game_scene/tutorial_manager.gd b/menus/scenes/game_scene/tutorial_manager.gd new file mode 100644 index 0000000..43e666c --- /dev/null +++ b/menus/scenes/game_scene/tutorial_manager.gd @@ -0,0 +1,27 @@ +extends Node +## A script to add into a level or game scene to display tutorial windows. + +## A list of tutorial scenes to open, one after the other. +@export var tutorial_scenes : Array[PackedScene] +## If true, open the tutorials when the scene becomes ready. +@export var auto_open : bool = false +## Delay before opening the tutorials when the scene becomes ready. +@export var auto_open_delay : float = 0.25 + +func open_tutorials() -> void: + var _initial_focus_control : Control = get_viewport().gui_get_focus_owner() + for tutorial_scene in tutorial_scenes: + var tutorial_menu : Control = tutorial_scene.instantiate() + if tutorial_menu == null: + push_warning("tutorial failed to open %s" % tutorial_scene) + return + get_tree().current_scene.call_deferred("add_child", tutorial_menu) + await tutorial_menu.tree_exited + if is_inside_tree() and _initial_focus_control: + _initial_focus_control.grab_focus() + +func _ready() -> void: + if auto_open: + if auto_open_delay > 0.0: + await get_tree().create_timer(auto_open_delay, false).timeout + open_tutorials.call_deferred() diff --git a/menus/scenes/game_scene/tutorial_manager.gd.uid b/menus/scenes/game_scene/tutorial_manager.gd.uid new file mode 100644 index 0000000..896490e --- /dev/null +++ b/menus/scenes/game_scene/tutorial_manager.gd.uid @@ -0,0 +1 @@ +uid://dnuloyvbcw7jd diff --git a/menus/scenes/game_scene/tutorials/tutorial_1.tscn b/menus/scenes/game_scene/tutorials/tutorial_1.tscn new file mode 100644 index 0000000..ac0e9cb --- /dev/null +++ b/menus/scenes/game_scene/tutorials/tutorial_1.tscn @@ -0,0 +1,19 @@ +[gd_scene format=3 uid="uid://ccho26xthu50o"] + +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/windows/overlaid_window.tscn" id="1_jp4os"] + +[node name="OverlaidWindow" instance=ExtResource("1_jp4os")] +custom_minimum_size = Vector2(420, 200) +update_content = true +text = "Click the Win button to progress. +Click the Lose button to try again." +title = "Tutorial" +title_font_size = 20 + +[node name="TitleLabel" parent="ContentContainer/BoxContainer/TitleMargin/BoxContainer" index="0"] +theme_override_font_sizes/font_size = 20 +text = "Tutorial" + +[node name="DescriptionLabel" parent="ContentContainer/BoxContainer/BodyMargin" index="0"] +text = "Click the Win button to progress. +Click the Lose button to try again." diff --git a/menus/scenes/game_scene/tutorials/tutorial_2.tscn b/menus/scenes/game_scene/tutorials/tutorial_2.tscn new file mode 100644 index 0000000..95f4c43 --- /dev/null +++ b/menus/scenes/game_scene/tutorials/tutorial_2.tscn @@ -0,0 +1,17 @@ +[gd_scene format=3 uid="uid://vv0uamr284l6"] + +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/windows/overlaid_window.tscn" id="1_83bj0"] + +[node name="OverlaidWindow" instance=ExtResource("1_83bj0")] +custom_minimum_size = Vector2(420, 200) +update_content = true +text = "Pressing Escape will open the Pause Menu." +title = "Tutorial" +title_font_size = 20 + +[node name="TitleLabel" parent="ContentContainer/BoxContainer/TitleMargin/BoxContainer" index="0"] +theme_override_font_sizes/font_size = 20 +text = "Tutorial" + +[node name="DescriptionLabel" parent="ContentContainer/BoxContainer/BodyMargin" index="0"] +text = "Pressing Escape will open the Pause Menu." diff --git a/menus/scenes/game_scene/tutorials/tutorial_3.tscn b/menus/scenes/game_scene/tutorials/tutorial_3.tscn new file mode 100644 index 0000000..fef16b1 --- /dev/null +++ b/menus/scenes/game_scene/tutorials/tutorial_3.tscn @@ -0,0 +1,17 @@ +[gd_scene format=3 uid="uid://bxxaeanswkvyy"] + +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/windows/overlaid_window.tscn" id="1_301qr"] + +[node name="OverlaidWindow" instance=ExtResource("1_301qr")] +custom_minimum_size = Vector2(420, 200) +update_content = true +text = "The label at the bottom-center displays the current input action detected, if any are setup for the project." +title = "Tutorial" +title_font_size = 20 + +[node name="TitleLabel" parent="ContentContainer/BoxContainer/TitleMargin/BoxContainer" index="0"] +theme_override_font_sizes/font_size = 20 +text = "Tutorial" + +[node name="DescriptionLabel" parent="ContentContainer/BoxContainer/BodyMargin" index="0"] +text = "The label at the bottom-center displays the current input action detected, if any are setup for the project." diff --git a/menus/scenes/menus/level_select_menu/level_select_menu.gd b/menus/scenes/menus/level_select_menu/level_select_menu.gd new file mode 100644 index 0000000..a373b24 --- /dev/null +++ b/menus/scenes/menus/level_select_menu/level_select_menu.gd @@ -0,0 +1,31 @@ +extends Control + +## Loads a simple ItemList node within a margin container. SceneLister updates +## the available scenes in the directory provided. Activating a level will update +## the GameState's current_level, and emit a signal. The main menu node will trigger +## a load action from that signal. + +signal level_selected + +@onready var level_buttons_container: ItemList = %LevelButtonsContainer +@onready var scene_lister: SceneLister = $SceneLister +var level_paths : Array[String] + +func _ready() -> void: + add_levels_to_container() + +## A fresh level list is propgated into the ItemList, and the file names are cleaned +func add_levels_to_container() -> void: + level_buttons_container.clear() + level_paths.clear() + for file_path in scene_lister.files: + var file_name : String = file_path.get_file() # e.g., "level_1.tscn" + file_name = file_name.trim_suffix(".tscn") # Remove the ".tscn" extension + file_name = file_name.replace("_", " ") # Replace underscores with spaces + file_name = file_name.capitalize() # Convert to proper case + var button_name := str(file_name) + level_buttons_container.add_item(button_name) + level_paths.append(file_path) + +func _on_level_buttons_container_item_activated(index: int) -> void: + level_selected.emit() diff --git a/menus/scenes/menus/level_select_menu/level_select_menu.gd.uid b/menus/scenes/menus/level_select_menu/level_select_menu.gd.uid new file mode 100644 index 0000000..3a50a19 --- /dev/null +++ b/menus/scenes/menus/level_select_menu/level_select_menu.gd.uid @@ -0,0 +1 @@ +uid://jylpc6gx3p3i diff --git a/menus/scenes/menus/level_select_menu/level_select_menu.tscn b/menus/scenes/menus/level_select_menu/level_select_menu.tscn new file mode 100644 index 0000000..fe06610 --- /dev/null +++ b/menus/scenes/menus/level_select_menu/level_select_menu.tscn @@ -0,0 +1,49 @@ +[gd_scene format=3 uid="uid://dsdir3jef5bwu"] + +[ext_resource type="Script" path="res://menus/scenes/menus/level_select_menu/level_select_menu.gd" id="1_421qk"] +[ext_resource type="Script" path="res://addons/maaacks_game_template/base/nodes/utilities/capture_focus.gd" id="2_7xilf"] +[ext_resource type="Script" path="res://addons/maaacks_game_template/extras/scripts/scene_lister.gd" id="3_6cc2i"] + +[node name="LevelSelectMenu" 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_421qk") + +[node name="Control" type="Control" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("2_7xilf") + +[node name="LevelButtonsContainer" type="ItemList" parent="Control"] +unique_name_in_owner = true +custom_minimum_size = Vector2(400, 0) +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -200.0 +offset_top = -17.5 +offset_right = 200.0 +offset_bottom = 17.5 +grow_horizontal = 2 +grow_vertical = 2 +auto_height = true +item_count = 1 +item_0/text = "1 - ExampleLevel" + +[node name="SceneLister" type="Node" parent="."] +script = ExtResource("3_6cc2i") +files = Array[String](["res://menus/scenes/game_scene/levels/level_1.tscn", "res://menus/scenes/game_scene/levels/level_2.tscn", "res://menus/scenes/game_scene/levels/level_3.tscn"]) +directory = "res://menus/scenes/game_scene/levels" + +[connection signal="item_activated" from="Control/LevelButtonsContainer" to="." method="_on_level_buttons_container_item_activated"] diff --git a/menus/scenes/menus/main_menu/main_menu.gd b/menus/scenes/menus/main_menu/main_menu.gd new file mode 100644 index 0000000..4c83b4f --- /dev/null +++ b/menus/scenes/menus/main_menu/main_menu.gd @@ -0,0 +1 @@ +extends MainMenu diff --git a/menus/scenes/menus/main_menu/main_menu.gd.uid b/menus/scenes/menus/main_menu/main_menu.gd.uid new file mode 100644 index 0000000..ba33a75 --- /dev/null +++ b/menus/scenes/menus/main_menu/main_menu.gd.uid @@ -0,0 +1 @@ +uid://g1m1u0savje4 diff --git a/menus/scenes/menus/main_menu/main_menu.tscn b/menus/scenes/menus/main_menu/main_menu.tscn new file mode 100644 index 0000000..19ada12 --- /dev/null +++ b/menus/scenes/menus/main_menu/main_menu.tscn @@ -0,0 +1,12 @@ +[gd_scene format=3 uid="uid://dhdr6yxca023l"] + +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/menus/main_menu/main_menu.tscn" id="1_6pasy"] +[ext_resource type="Script" path="res://menus/scenes/menus/main_menu/main_menu.gd" id="2_i8spl"] +[ext_resource type="PackedScene" path="res://menus/scenes/windows/main_menu_options_window.tscn" id="3_qe0nm"] +[ext_resource type="PackedScene" path="res://menus/scenes/windows/main_menu_credits_window.tscn" id="4_mov4g"] + +[node name="MainMenu" instance=ExtResource("1_6pasy")] +script = ExtResource("2_i8spl") +game_scene_path = "res://menus/scenes/game_scene/game_ui.tscn" +options_packed_scene = ExtResource("3_qe0nm") +credits_packed_scene = ExtResource("4_mov4g") diff --git a/menus/scenes/menus/main_menu/main_menu_with_animations.gd b/menus/scenes/menus/main_menu/main_menu_with_animations.gd new file mode 100644 index 0000000..d14069c --- /dev/null +++ b/menus/scenes/menus/main_menu/main_menu_with_animations.gd @@ -0,0 +1,35 @@ +extends MainMenu +## Main menu extension that animates the title and menu fading in. +## The animation can be skipped by the player with any input. + +var animation_state_machine : AnimationNodeStateMachinePlayback + +func intro_done() -> void: + animation_state_machine.travel("OpenMainMenu") + +func _is_in_intro() -> bool: + return animation_state_machine.get_current_node() == "Intro" + +func _event_skips_intro(event : InputEvent) -> bool: + return event.is_action_released("ui_accept") or \ + event.is_action_released("ui_select") or \ + event.is_action_released("ui_cancel") or \ + _event_is_mouse_button_released(event) + +func _open_sub_menu(menu : PackedScene) -> Node: + animation_state_machine.travel("OpenSubMenu") + return super._open_sub_menu(menu) + +func _close_sub_menu() -> void: + super._close_sub_menu() + animation_state_machine.travel("OpenMainMenu") + +func _input(event : InputEvent) -> void: + if _is_in_intro() and _event_skips_intro(event): + intro_done() + return + super._input(event) + +func _ready() -> void: + super._ready() + animation_state_machine = $MenuAnimationTree.get("parameters/playback") diff --git a/menus/scenes/menus/main_menu/main_menu_with_animations.gd.uid b/menus/scenes/menus/main_menu/main_menu_with_animations.gd.uid new file mode 100644 index 0000000..4d1ea0c --- /dev/null +++ b/menus/scenes/menus/main_menu/main_menu_with_animations.gd.uid @@ -0,0 +1 @@ +uid://cgj703gjwdvs1 diff --git a/menus/scenes/menus/main_menu/main_menu_with_animations.tscn b/menus/scenes/menus/main_menu/main_menu_with_animations.tscn new file mode 100644 index 0000000..897728b --- /dev/null +++ b/menus/scenes/menus/main_menu/main_menu_with_animations.tscn @@ -0,0 +1,356 @@ +[gd_scene format=3 uid="uid://cxeuh57i33qrx"] + +[ext_resource type="PackedScene" uid="uid://c6k5nnpbypshi" path="res://addons/maaacks_game_template/base/nodes/menus/main_menu/main_menu.tscn" id="1_sbi1w"] +[ext_resource type="Script" uid="uid://cgj703gjwdvs1" path="res://menus/scenes/menus/main_menu/main_menu_with_animations.gd" id="2_ytpwg"] +[ext_resource type="PackedScene" uid="uid://xkqu58vkydps" path="res://menus/scenes/windows/main_menu_options_window.tscn" id="3_8i6b8"] +[ext_resource type="PackedScene" uid="uid://cvweffl7bo7bj" path="res://menus/scenes/windows/main_menu_credits_window.tscn" id="4_t55r5"] + +[sub_resource type="Animation" id="1"] +resource_name = "Intro" +length = 2.4 +tracks/0/type = "method" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(2.4), +"transitions": PackedFloat32Array(1), +"values": [{ +"args": [], +"method": &"intro_done" +}] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("MenuContainer/TitleMargin/TitleContainer:modulate") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0, 0.8), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [Color(1, 1, 1, 0), Color(1, 1, 1, 1)] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("MenuContainer/SubTitleMargin/SubTitleContainer:modulate") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0, 0.8, 1.6), +"transitions": PackedFloat32Array(1, 1, 1), +"update": 0, +"values": [Color(1, 1, 1, 0), Color(1, 1, 1, 0), Color(1, 1, 1, 1)] +} +tracks/3/type = "value" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath("MenuContainer/MenuButtonsMargin/MenuButtonsContainer:modulate") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"times": PackedFloat32Array(0, 1.6, 2.4), +"transitions": PackedFloat32Array(1, 1, 1), +"update": 0, +"values": [Color(1, 1, 1, 0), Color(1, 1, 1, 0), Color(1, 1, 1, 1)] +} +tracks/4/type = "value" +tracks/4/imported = false +tracks/4/enabled = true +tracks/4/path = NodePath("MouseFilter:mouse_filter") +tracks/4/interp = 1 +tracks/4/loop_wrap = true +tracks/4/keys = { +"times": PackedFloat32Array(0, 2.4), +"transitions": PackedFloat32Array(1, 1), +"update": 1, +"values": [0, 2] +} +tracks/5/type = "value" +tracks/5/imported = false +tracks/5/enabled = true +tracks/5/path = NodePath("VersionMargin/VersionContainer:modulate") +tracks/5/interp = 1 +tracks/5/loop_wrap = true +tracks/5/keys = { +"times": PackedFloat32Array(0, 1.6, 2.4), +"transitions": PackedFloat32Array(1, 1, 1), +"update": 0, +"values": [Color(1, 1, 1, 0), Color(1, 1, 1, 0), Color(1, 1, 1, 1)] +} + +[sub_resource type="Animation" id="6"] +resource_name = "OpenMainMenu" +length = 0.1 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("MenuContainer/TitleMargin/TitleContainer:modulate") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(1, 1, 1, 1)] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("MenuContainer/SubTitleMargin/SubTitleContainer:modulate") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(1, 1, 1, 1)] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("MenuContainer/MenuButtonsMargin/MenuButtonsContainer:modulate") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(1, 1, 1, 1)] +} +tracks/3/type = "value" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath("MouseFilter:mouse_filter") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [2] +} +tracks/4/type = "value" +tracks/4/imported = false +tracks/4/enabled = true +tracks/4/path = NodePath("MenuContainer:modulate") +tracks/4/interp = 1 +tracks/4/loop_wrap = true +tracks/4/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(1, 1, 1, 1)] +} +tracks/5/type = "value" +tracks/5/imported = false +tracks/5/enabled = true +tracks/5/path = NodePath("VersionMargin/VersionContainer:modulate") +tracks/5/interp = 1 +tracks/5/loop_wrap = true +tracks/5/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(1, 1, 1, 1)] +} +tracks/6/type = "value" +tracks/6/imported = false +tracks/6/enabled = true +tracks/6/path = NodePath("MenuContainer/MenuButtonsMargin/MenuButtonsContainer/MenuButtonsBoxContainer:lock") +tracks/6/interp = 1 +tracks/6/loop_wrap = true +tracks/6/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [false] +} + +[sub_resource type="Animation" id="4"] +resource_name = "OpenSubMenu" +length = 0.2 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("MenuContainer:modulate") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(1, 1, 1, 0)] +} + +[sub_resource type="Animation" id="2"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("MenuContainer/TitleMargin/TitleContainer:modulate") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(1, 1, 1, 0)] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("MenuContainer/SubTitleMargin/SubTitleContainer:modulate") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(1, 1, 1, 0)] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("MenuContainer/MenuButtonsMargin/MenuButtonsContainer:modulate") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(1, 1, 1, 0)] +} +tracks/3/type = "value" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath("MouseFilter:mouse_filter") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [2] +} +tracks/4/type = "value" +tracks/4/imported = false +tracks/4/enabled = true +tracks/4/path = NodePath("MenuContainer:modulate") +tracks/4/interp = 1 +tracks/4/loop_wrap = true +tracks/4/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(1, 1, 1, 1)] +} +tracks/5/type = "value" +tracks/5/imported = false +tracks/5/enabled = true +tracks/5/path = NodePath("VersionMargin/VersionContainer:modulate") +tracks/5/interp = 1 +tracks/5/loop_wrap = true +tracks/5/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(1, 1, 1, 0)] +} +tracks/6/type = "value" +tracks/6/imported = false +tracks/6/enabled = true +tracks/6/path = NodePath("MenuContainer/MenuButtonsMargin/MenuButtonsContainer/MenuButtonsBoxContainer:lock") +tracks/6/interp = 1 +tracks/6/loop_wrap = true +tracks/6/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [true] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_2kqig"] +_data = { +&"Intro": SubResource("1"), +&"OpenMainMenu": SubResource("6"), +&"OpenSubMenu": SubResource("4"), +&"RESET": SubResource("2") +} + +[sub_resource type="AnimationNodeAnimation" id="7"] +animation = &"Intro" + +[sub_resource type="AnimationNodeAnimation" id="10"] +animation = &"OpenMainMenu" + +[sub_resource type="AnimationNodeAnimation" id="13"] +animation = &"OpenSubMenu" + +[sub_resource type="AnimationNodeStateMachineTransition" id="11"] +advance_condition = &"intro_done" + +[sub_resource type="AnimationNodeStateMachineTransition" id="14"] + +[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_j0orr"] +advance_mode = 2 + +[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_63dxc"] + +[sub_resource type="AnimationNodeStateMachine" id="AnimationNodeStateMachine_vikuh"] +states/End/position = Vector2(958, 123) +states/Intro/node = SubResource("7") +states/Intro/position = Vector2(259, 123) +states/OpenMainMenu/node = SubResource("10") +states/OpenMainMenu/position = Vector2(472, 123) +states/OpenSubMenu/node = SubResource("13") +states/OpenSubMenu/position = Vector2(734, 123) +states/Start/position = Vector2(82, 123) +transitions = ["Intro", "OpenMainMenu", SubResource("11"), "OpenMainMenu", "OpenSubMenu", SubResource("14"), "Start", "Intro", SubResource("AnimationNodeStateMachineTransition_j0orr"), "OpenSubMenu", "OpenMainMenu", SubResource("AnimationNodeStateMachineTransition_63dxc")] +graph_offset = Vector2(-180.277, 49) + +[node name="MainMenu" unique_id=1582294538 instance=ExtResource("1_sbi1w")] +script = ExtResource("2_ytpwg") +game_scene_path = "uid://cukfdjcnb5tm6" +options_packed_scene = ExtResource("3_8i6b8") +credits_packed_scene = ExtResource("4_t55r5") + +[node name="MenuAnimationPlayer" type="AnimationPlayer" parent="." index="1" unique_id=53025128] +libraries/ = SubResource("AnimationLibrary_2kqig") + +[node name="MenuAnimationTree" type="AnimationTree" parent="." index="2" unique_id=1694007958] +tree_root = SubResource("AnimationNodeStateMachine_vikuh") +anim_player = NodePath("../MenuAnimationPlayer") +parameters/conditions/intro_done = false + +[node name="TitleContainer" parent="MenuContainer/TitleMargin" index="0"] +modulate = Color(1, 1, 1, 0) + +[node name="TitleLabel" parent="MenuContainer/TitleMargin/TitleContainer" index="0"] +text = "GGJ26" + +[node name="SubTitleContainer" parent="MenuContainer/SubTitleMargin" index="0"] +modulate = Color(1, 1, 1, 0) + +[node name="MenuButtonsContainer" parent="MenuContainer/MenuButtonsMargin" index="0"] +modulate = Color(1, 1, 1, 0) + +[node name="MenuButtonsBoxContainer" parent="MenuContainer/MenuButtonsMargin/MenuButtonsContainer" index="0"] +lock = true + +[node name="VersionContainer" parent="VersionMargin" index="0"] +modulate = Color(1, 1, 1, 0) + +[node name="MouseFilter" type="Control" parent="." index="6" unique_id=420264660] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +metadata/_edit_lock_ = true diff --git a/menus/scenes/menus/options_menu/audio/audio_input_option_control.gd b/menus/scenes/menus/options_menu/audio/audio_input_option_control.gd new file mode 100644 index 0000000..da3199a --- /dev/null +++ b/menus/scenes/menus/options_menu/audio/audio_input_option_control.gd @@ -0,0 +1,38 @@ +@tool +extends ListOptionControl + +func _set_input_device() -> void: + var current_setting : Variant = _get_setting(default_value) + if current_setting is bool: + current_setting = &"Default" + AudioServer.input_device = _get_setting(default_value) + +func _add_microphone_audio_stream() -> void: + var instance := AudioStreamPlayer.new() + instance.stream = AudioStreamMicrophone.new() + instance.autoplay = true + add_child.call_deferred(instance) + instance.ready.connect(_set_input_device) + +func _ready() -> void: + if ProjectSettings.get_setting("audio/driver/enable_input", false): + if AudioServer.input_device.is_empty(): + _add_microphone_audio_stream() + else: + _set_input_device() + if not Engine.is_editor_hint(): + option_values = AudioServer.get_input_device_list() + else: + hide() + super._ready() + +func _on_setting_changed(value : Variant) -> void: + if value >= option_values.size(): return + AudioServer.input_device = option_values[value] + super._on_setting_changed(value) + +func _value_title_map(value : Variant) -> String: + if value is String: + return value + else: + return super._value_title_map(value) diff --git a/menus/scenes/menus/options_menu/audio/audio_input_option_control.gd.uid b/menus/scenes/menus/options_menu/audio/audio_input_option_control.gd.uid new file mode 100644 index 0000000..a6f3663 --- /dev/null +++ b/menus/scenes/menus/options_menu/audio/audio_input_option_control.gd.uid @@ -0,0 +1 @@ +uid://dynv5vb7jhb5t diff --git a/menus/scenes/menus/options_menu/audio/audio_input_option_control.tscn b/menus/scenes/menus/options_menu/audio/audio_input_option_control.tscn new file mode 100644 index 0000000..4267252 --- /dev/null +++ b/menus/scenes/menus/options_menu/audio/audio_input_option_control.tscn @@ -0,0 +1,20 @@ +[gd_scene format=3 uid="uid://cix0301eivn0f"] + +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/list_option_control.tscn" id="1_07rmo"] +[ext_resource type="Script" path="res://menus/scenes/menus/options_menu/audio/audio_input_option_control.gd" id="2_l8m0l"] + +[node name="AudioInputOptionControl" instance=ExtResource("1_07rmo")] +script = ExtResource("2_l8m0l") +option_name = "Input Device" +option_section = 2 +key = "InputDevice" +section = "AudioSettings" +property_type = 4 + +[node name="OptionLabel" parent="." index="0"] +text = "Input Device :" + +[node name="OptionButton" parent="." index="1"] +size_flags_horizontal = 3 +text_overrun_behavior = 1 +clip_text = true diff --git a/menus/scenes/menus/options_menu/audio/audio_options_menu.tscn b/menus/scenes/menus/options_menu/audio/audio_options_menu.tscn new file mode 100644 index 0000000..1247fcc --- /dev/null +++ b/menus/scenes/menus/options_menu/audio/audio_options_menu.tscn @@ -0,0 +1,9 @@ +[gd_scene format=3 uid="uid://cml3v864u2ofy"] + +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/menus/options_menu/audio/audio_options_menu.tscn" id="1_nu6ah"] +[ext_resource type="PackedScene" path="res://menus/scenes/menus/options_menu/audio/audio_input_option_control.tscn" id="2_40jc8"] + +[node name="Audio" instance=ExtResource("1_nu6ah")] + +[node name="AudioInputOptionControl" parent="VBoxContainer" index="2" instance=ExtResource("2_40jc8")] +layout_mode = 2 diff --git a/menus/scenes/menus/options_menu/input/input_extras_menu.tscn b/menus/scenes/menus/options_menu/input/input_extras_menu.tscn new file mode 100644 index 0000000..8d8dad9 --- /dev/null +++ b/menus/scenes/menus/options_menu/input/input_extras_menu.tscn @@ -0,0 +1,65 @@ +[gd_scene format=3 uid="uid://bd1ibe02iae8k"] + +[ext_resource type="Script" path="res://addons/maaacks_game_template/base/nodes/utilities/capture_focus.gd" id="1_jvcy6"] +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/slider_option_control.tscn" id="2_qe5tt"] + +[node name="Inputs" 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 + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +theme_override_constants/separation = 8 +script = ExtResource("1_jvcy6") +search_depth = 5 + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer"] +layout_mode = 2 +theme_override_constants/margin_top = 32 +theme_override_constants/margin_bottom = 32 + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/MarginContainer"] +layout_mode = 2 +size_flags_vertical = 3 +theme_override_constants/separation = 8 +alignment = 1 + +[node name="MouseSensitivityControl" parent="VBoxContainer/MarginContainer/VBoxContainer" instance=ExtResource("2_qe5tt")] +layout_mode = 2 +option_name = "Mouse Sensitivity" +option_section = 1 +key = "MouseSensitivity" +section = "InputSettings" + +[node name="OptionLabel" parent="VBoxContainer/MarginContainer/VBoxContainer/MouseSensitivityControl" index="0"] +text = "Mouse Sensitivity :" + +[node name="HSlider" parent="VBoxContainer/MarginContainer/VBoxContainer/MouseSensitivityControl" index="1"] +min_value = 0.25 +max_value = 2.0 +tick_count = 8 + +[node name="JoypadSensitivityControl" parent="VBoxContainer/MarginContainer/VBoxContainer" instance=ExtResource("2_qe5tt")] +layout_mode = 2 +option_name = "Joypad Sensitivity" +option_section = 1 +key = "JoypadSensitivity" +section = "InputSettings" + +[node name="OptionLabel" parent="VBoxContainer/MarginContainer/VBoxContainer/JoypadSensitivityControl" index="0"] +text = "Joypad Sensitivity :" + +[node name="HSlider" parent="VBoxContainer/MarginContainer/VBoxContainer/JoypadSensitivityControl" index="1"] +min_value = 0.25 +max_value = 2.0 +tick_count = 8 + +[editable path="VBoxContainer/MarginContainer/VBoxContainer/MouseSensitivityControl"] +[editable path="VBoxContainer/MarginContainer/VBoxContainer/JoypadSensitivityControl"] diff --git a/menus/scenes/menus/options_menu/input/input_icon_mapper.tscn b/menus/scenes/menus/options_menu/input/input_icon_mapper.tscn new file mode 100644 index 0000000..24ee5ca --- /dev/null +++ b/menus/scenes/menus/options_menu/input/input_icon_mapper.tscn @@ -0,0 +1,5 @@ +[gd_scene format=3 uid="uid://dpmvdprece0yg"] + +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_icon_mapper.tscn" id="1_uqe1v"] + +[node name="InputIconMapper" instance=ExtResource("1_uqe1v")] diff --git a/menus/scenes/menus/options_menu/input/input_options_menu.tscn b/menus/scenes/menus/options_menu/input/input_options_menu.tscn new file mode 100644 index 0000000..70b1921 --- /dev/null +++ b/menus/scenes/menus/options_menu/input/input_options_menu.tscn @@ -0,0 +1,14 @@ +[gd_scene format=3 uid="uid://vbk4q2e0kwen"] + +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_options_menu.tscn" id="1_hxdak"] +[ext_resource type="PackedScene" path="res://menus/scenes/menus/options_menu/input/input_icon_mapper.tscn" id="2_5jnsn"] + +[node name="Controls" instance=ExtResource("1_hxdak")] + +[node name="InputActionsList" parent="VBoxContainer/InputMappingContainer" index="1" node_paths=PackedStringArray("input_icon_mapper")] +input_icon_mapper = NodePath("../../../InputIconMapper") + +[node name="InputActionsTree" parent="VBoxContainer/InputMappingContainer" index="2" node_paths=PackedStringArray("input_icon_mapper")] +input_icon_mapper = NodePath("../../../InputIconMapper") + +[node name="InputIconMapper" parent="." index="6" instance=ExtResource("2_5jnsn")] diff --git a/menus/scenes/menus/options_menu/input/input_options_menu_with_mouse_sensitivity.tscn b/menus/scenes/menus/options_menu/input/input_options_menu_with_mouse_sensitivity.tscn new file mode 100644 index 0000000..375206d --- /dev/null +++ b/menus/scenes/menus/options_menu/input/input_options_menu_with_mouse_sensitivity.tscn @@ -0,0 +1,39 @@ +[gd_scene format=3 uid="uid://cuakr0787n2fg"] + +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/menus/options_menu/input/input_options_menu.tscn" id="1_40c2t"] +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/slider_option_control.tscn" id="2_6biia"] + +[node name="Controls" instance=ExtResource("1_40c2t")] + +[node name="VBoxContainer" parent="." index="0"] +theme_override_constants/separation = 16 + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer" index="0"] +layout_mode = 2 +theme_override_constants/margin_top = 32 +theme_override_constants/margin_bottom = 32 + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/MarginContainer" index="0"] +layout_mode = 2 +size_flags_vertical = 3 +alignment = 1 + +[node name="MouseSensitivityControl" parent="VBoxContainer/MarginContainer/VBoxContainer" index="0" instance=ExtResource("2_6biia")] +layout_mode = 2 +option_name = "Mouse Sensitivity" +option_section = 1 +key = "MouseSensitivity" +section = "InputSettings" + +[node name="OptionLabel" parent="VBoxContainer/MarginContainer/VBoxContainer/MouseSensitivityControl" index="0"] +text = "Mouse Sensitivity :" + +[node name="HSlider" parent="VBoxContainer/MarginContainer/VBoxContainer/MouseSensitivityControl" index="1"] +min_value = 0.25 +max_value = 2.0 +tick_count = 8 + +[node name="HSeparator" type="HSeparator" parent="VBoxContainer" index="1"] +layout_mode = 2 + +[editable path="VBoxContainer/MarginContainer/VBoxContainer/MouseSensitivityControl"] diff --git a/menus/scenes/menus/options_menu/master_options_menu_with_tabs.tscn b/menus/scenes/menus/options_menu/master_options_menu_with_tabs.tscn new file mode 100644 index 0000000..22d34dc --- /dev/null +++ b/menus/scenes/menus/options_menu/master_options_menu_with_tabs.tscn @@ -0,0 +1,36 @@ +[gd_scene format=3 uid="uid://dsh4piv0l7u4u"] + +[ext_resource type="Script" path="res://addons/maaacks_game_template/base/nodes/menus/options_menu/paginated_tab_container.gd" id="1_ai8ld"] +[ext_resource type="PackedScene" path="res://menus/scenes/menus/options_menu/input/input_options_menu.tscn" id="2_03b4v"] +[ext_resource type="PackedScene" path="res://menus/scenes/menus/options_menu/input/input_extras_menu.tscn" id="3_amlig"] +[ext_resource type="PackedScene" path="res://menus/scenes/menus/options_menu/audio/audio_options_menu.tscn" id="4_1q2u1"] +[ext_resource type="PackedScene" path="res://menus/scenes/menus/options_menu/video/video_options_menu_with_extras.tscn" id="5_uvcwx"] + +[node name="MasterOptionsMenu" type="TabContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +tab_alignment = 1 +current_tab = 0 +script = ExtResource("1_ai8ld") + +[node name="Controls" parent="." instance=ExtResource("2_03b4v")] +layout_mode = 2 +metadata/_tab_index = 0 + +[node name="Inputs" parent="." instance=ExtResource("3_amlig")] +visible = false +layout_mode = 2 +metadata/_tab_index = 1 + +[node name="Audio" parent="." instance=ExtResource("4_1q2u1")] +visible = false +layout_mode = 2 +metadata/_tab_index = 2 + +[node name="Video" parent="." instance=ExtResource("5_uvcwx")] +visible = false +layout_mode = 2 +metadata/_tab_index = 3 diff --git a/menus/scenes/menus/options_menu/mini_options_menu.tscn b/menus/scenes/menus/options_menu/mini_options_menu.tscn new file mode 100644 index 0000000..e5fa7c5 --- /dev/null +++ b/menus/scenes/menus/options_menu/mini_options_menu.tscn @@ -0,0 +1,51 @@ +[gd_scene format=3 uid="uid://cibq68uel4bcn"] + +[ext_resource type="Script" path="res://addons/maaacks_game_template/base/nodes/menus/options_menu/mini_options_menu.gd" id="1_1omja"] +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/slider_option_control.tscn" id="2_c8myg"] +[ext_resource type="Script" path="res://addons/maaacks_game_template/base/nodes/utilities/capture_focus.gd" id="3_luqg7"] +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/toggle_option_control.tscn" id="4_u2ks0"] + +[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_1omja") +audio_control_scene = ExtResource("2_c8myg") + +[node name="AudioControlContainer" type="VBoxContainer" parent="."] +unique_name_in_owner = true +layout_mode = 2 +theme_override_constants/separation = 8 +script = ExtResource("3_luqg7") +search_depth = 2 + +[node name="MuteControl" parent="." instance=ExtResource("4_u2ks0")] +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_u2ks0")] +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"] diff --git a/menus/scenes/menus/options_menu/video/video_options_menu.tscn b/menus/scenes/menus/options_menu/video/video_options_menu.tscn new file mode 100644 index 0000000..a8a0dda --- /dev/null +++ b/menus/scenes/menus/options_menu/video/video_options_menu.tscn @@ -0,0 +1,5 @@ +[gd_scene format=3 uid="uid://c5enlypne8pnc"] + +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/menus/options_menu/video/video_options_menu.tscn" id="1_48urb"] + +[node name="Video" instance=ExtResource("1_48urb")] diff --git a/menus/scenes/menus/options_menu/video/video_options_menu_with_extras.tscn b/menus/scenes/menus/options_menu/video/video_options_menu_with_extras.tscn new file mode 100644 index 0000000..626da89 --- /dev/null +++ b/menus/scenes/menus/options_menu/video/video_options_menu_with_extras.tscn @@ -0,0 +1,31 @@ +[gd_scene format=3 uid="uid://4hsiqr4ehbbq"] + +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/menus/options_menu/video/video_options_menu.tscn" id="1_rdxho"] +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/menus/options_menu/option_control/list_option_control.tscn" id="2_0u1ef"] + +[node name="Video" instance=ExtResource("1_rdxho")] + +[node name="AntiAliasingControl" parent="VBoxContainer" index="3" instance=ExtResource("2_0u1ef")] +layout_mode = 2 +lock_titles = true +option_values = [0, 1, 2, 3] +option_titles = Array[String](["Disabled (Fastest)", "2x", "4x", "8x (Slowest)"]) +option_name = "Anti-Aliasing" +option_section = 3 +key = "Anti-aliasing" +section = "VideoSettings" +property_type = 2 +default_value = 0 + +[node name="CameraShakeControl" parent="VBoxContainer" index="4" instance=ExtResource("2_0u1ef")] +visible = false +layout_mode = 2 +lock_titles = true +option_values = [1.0, 0.75, 0.5, 0.0] +option_titles = Array[String](["Normal", "Reduced", "Minimal", "None"]) +option_name = "Camera Shake" +option_section = 3 +key = "CameraShake" +section = "VideoSettings" +property_type = 3 +default_value = 1.0 diff --git a/menus/scenes/opening/opening.tscn b/menus/scenes/opening/opening.tscn new file mode 100644 index 0000000..b05c747 --- /dev/null +++ b/menus/scenes/opening/opening.tscn @@ -0,0 +1,14 @@ +[gd_scene format=3 uid="uid://cujhvnbd2sfkw"] + +[ext_resource type="PackedScene" uid="uid://sikc02ddepyt" path="res://addons/maaacks_game_template/base/nodes/opening/opening.tscn" id="1_luwk1"] +[ext_resource type="Texture2D" uid="uid://4hasb7yih53v" path="res://menus/assets/godot_engine_logo/logo_vertical_color_dark.png" id="2_cj7fe"] + +[node name="Opening" unique_id=24920084 instance=ExtResource("1_luwk1")] +next_scene_path = "uid://cxeuh57i33qrx" +images = Array[Texture2D]([ExtResource("2_cj7fe")]) + +[node name="ImagesContainer" parent="." index="0"] +theme_override_constants/margin_left = 64 +theme_override_constants/margin_top = 64 +theme_override_constants/margin_right = 64 +theme_override_constants/margin_bottom = 64 diff --git a/menus/scenes/windows/game_won_window.gd b/menus/scenes/windows/game_won_window.gd new file mode 100644 index 0000000..21707a7 --- /dev/null +++ b/menus/scenes/windows/game_won_window.gd @@ -0,0 +1,26 @@ +@tool +extends OverlaidWindow + +signal continue_pressed +signal main_menu_pressed + +func _ready(): + if OS.has_feature("web"): + %ExitButton.hide() + +func _on_exit_button_pressed(): + %ExitConfirmation.show() + +func _on_main_menu_button_pressed(): + %MainMenuConfirmation.show() + +func _on_close_button_pressed(): + continue_pressed.emit() + close() + +func _on_main_menu_confirmation_confirmed(): + main_menu_pressed.emit() + close() + +func _on_exit_confirmation_confirmed(): + get_tree().quit() diff --git a/menus/scenes/windows/game_won_window.gd.uid b/menus/scenes/windows/game_won_window.gd.uid new file mode 100644 index 0000000..0ff1107 --- /dev/null +++ b/menus/scenes/windows/game_won_window.gd.uid @@ -0,0 +1 @@ +uid://cwab7rbiob2ca diff --git a/menus/scenes/windows/game_won_window.tscn b/menus/scenes/windows/game_won_window.tscn new file mode 100644 index 0000000..5040d46 --- /dev/null +++ b/menus/scenes/windows/game_won_window.tscn @@ -0,0 +1,60 @@ +[gd_scene format=3 uid="uid://bivbf27ymoluy"] + +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/windows/overlaid_window.tscn" id="1_h0vmg"] +[ext_resource type="Script" path="res://menus/scenes/windows/game_won_window.gd" id="2_a248v"] +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/windows/confirmation_overlaid_window.tscn" id="3_dgrgs"] + +[node name="GameWonWindow" instance=ExtResource("1_h0vmg")] +custom_minimum_size = Vector2(432, 240) +script = ExtResource("2_a248v") +update_content = true +text = "You won!" +close_button_text = "Continue" +title_visible = false + +[node name="TitleMargin" parent="ContentContainer/BoxContainer" index="0"] +visible = false + +[node name="DescriptionLabel" parent="ContentContainer/BoxContainer/BodyMargin" index="0"] +text = "You won!" + +[node name="MenuButtons" parent="ContentContainer/BoxContainer/MenuButtonsMargin" index="0"] +vertical = false + +[node name="ExitButton" type="Button" parent="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="0"] +unique_name_in_owner = true +visible = false +layout_mode = 2 +size_flags_horizontal = 3 +text = "Exit" + +[node name="MainMenuButton" type="Button" parent="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="1"] +unique_name_in_owner = true +visible = false +layout_mode = 2 +size_flags_horizontal = 3 +text = "Main Menu" + +[node name="CloseButton" parent="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="2"] +size_flags_horizontal = 3 +text = "Continue" + +[node name="MainMenuConfirmation" parent="." index="1" instance=ExtResource("3_dgrgs")] +unique_name_in_owner = true +visible = false +custom_minimum_size = Vector2(220, 0) +layout_mode = 2 +text = "Exit to the main menu?" +title = "Confirm Exit" + +[node name="ExitConfirmation" parent="." index="2" instance=ExtResource("3_dgrgs")] +unique_name_in_owner = true +visible = false +layout_mode = 2 +text = "Exit the game?" +title = "Confirm Exit" + +[connection signal="pressed" from="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons/ExitButton" to="." method="_on_exit_button_pressed"] +[connection signal="pressed" from="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons/MainMenuButton" to="." method="_on_main_menu_button_pressed"] +[connection signal="confirmed" from="MainMenuConfirmation" to="." method="_on_main_menu_confirmation_confirmed"] +[connection signal="confirmed" from="ExitConfirmation" to="." method="_on_exit_confirmation_confirmed"] diff --git a/menus/scenes/windows/level_lost_window.gd b/menus/scenes/windows/level_lost_window.gd new file mode 100644 index 0000000..7405ea8 --- /dev/null +++ b/menus/scenes/windows/level_lost_window.gd @@ -0,0 +1,26 @@ +@tool +extends OverlaidWindow + +signal restart_pressed +signal main_menu_pressed + +func _ready(): + if OS.has_feature("web"): + %ExitButton.hide() + +func _on_exit_button_pressed(): + %ExitConfirmation.show() + +func _on_main_menu_button_pressed(): + %MainMenuConfirmation.show() + +func _on_close_button_pressed(): + restart_pressed.emit() + close() + +func _on_main_menu_confirmation_confirmed(): + main_menu_pressed.emit() + close() + +func _on_exit_confirmation_confirmed(): + get_tree().quit() diff --git a/menus/scenes/windows/level_lost_window.gd.uid b/menus/scenes/windows/level_lost_window.gd.uid new file mode 100644 index 0000000..db40ef0 --- /dev/null +++ b/menus/scenes/windows/level_lost_window.gd.uid @@ -0,0 +1 @@ +uid://bbffpw7vyygic diff --git a/menus/scenes/windows/level_lost_window.tscn b/menus/scenes/windows/level_lost_window.tscn new file mode 100644 index 0000000..00c96c1 --- /dev/null +++ b/menus/scenes/windows/level_lost_window.tscn @@ -0,0 +1,58 @@ +[gd_scene format=3 uid="uid://bimlvpibjpem0"] + +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/windows/overlaid_window.tscn" id="1_k3yu0"] +[ext_resource type="Script" path="res://menus/scenes/windows/level_lost_window.gd" id="2_u8wtw"] +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/windows/confirmation_overlaid_window.tscn" id="3_pir6e"] + +[node name="LevelLostWindow" instance=ExtResource("1_k3yu0")] +custom_minimum_size = Vector2(432, 240) +script = ExtResource("2_u8wtw") +update_content = true +text = "You lost..." +close_button_text = "Restart" +title_visible = false + +[node name="TitleMargin" parent="ContentContainer/BoxContainer" index="0"] +visible = false + +[node name="DescriptionLabel" parent="ContentContainer/BoxContainer/BodyMargin" index="0"] +text = "You lost..." + +[node name="MenuButtons" parent="ContentContainer/BoxContainer/MenuButtonsMargin" index="0"] +vertical = false + +[node name="ExitButton" type="Button" parent="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="0"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +text = "Exit" + +[node name="MainMenuButton" type="Button" parent="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="1"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +text = "Main Menu" + +[node name="CloseButton" parent="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="2"] +size_flags_horizontal = 3 +text = "Restart" + +[node name="MainMenuConfirmation" parent="." index="1" instance=ExtResource("3_pir6e")] +unique_name_in_owner = true +visible = false +custom_minimum_size = Vector2(220, 0) +layout_mode = 2 +text = "Exit to the main menu?" +title = "Confirm Exit" + +[node name="ExitConfirmation" parent="." index="2" instance=ExtResource("3_pir6e")] +unique_name_in_owner = true +visible = false +layout_mode = 2 +text = "Exit the game?" +title = "Confirm Exit" + +[connection signal="pressed" from="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons/ExitButton" to="." method="_on_exit_button_pressed"] +[connection signal="pressed" from="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons/MainMenuButton" to="." method="_on_main_menu_button_pressed"] +[connection signal="confirmed" from="MainMenuConfirmation" to="." method="_on_main_menu_confirmation_confirmed"] +[connection signal="confirmed" from="ExitConfirmation" to="." method="_on_exit_confirmation_confirmed"] diff --git a/menus/scenes/windows/level_won_window.gd b/menus/scenes/windows/level_won_window.gd new file mode 100644 index 0000000..23ce670 --- /dev/null +++ b/menus/scenes/windows/level_won_window.gd @@ -0,0 +1,31 @@ +@tool +extends OverlaidWindow + +signal continue_pressed +signal main_menu_pressed +signal restart_pressed + +func _ready(): + if OS.has_feature("web"): + %ExitButton.hide() + +func _on_exit_button_pressed(): + %ExitConfirmation.show() + +func _on_main_menu_button_pressed(): + %MainMenuConfirmation.show() + +func _on_close_button_pressed(): + continue_pressed.emit() + close() + +func _on_main_menu_confirmation_confirmed(): + main_menu_pressed.emit() + close() + +func _on_restart_button_pressed(): + restart_pressed.emit() + close() + +func _on_exit_confirmation_confirmed(): + get_tree().quit() diff --git a/menus/scenes/windows/level_won_window.gd.uid b/menus/scenes/windows/level_won_window.gd.uid new file mode 100644 index 0000000..783d74c --- /dev/null +++ b/menus/scenes/windows/level_won_window.gd.uid @@ -0,0 +1 @@ +uid://uuspingcbajg diff --git a/menus/scenes/windows/level_won_window.tscn b/menus/scenes/windows/level_won_window.tscn new file mode 100644 index 0000000..bf30971 --- /dev/null +++ b/menus/scenes/windows/level_won_window.tscn @@ -0,0 +1,66 @@ +[gd_scene format=3 uid="uid://q2oum0xhreo8"] + +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/windows/overlaid_window.tscn" id="1_wtikh"] +[ext_resource type="Script" path="res://menus/scenes/windows/level_won_window.gd" id="2_tsvun"] +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/windows/confirmation_overlaid_window.tscn" id="3_kw6pv"] + +[node name="LevelWonWindow" instance=ExtResource("1_wtikh")] +custom_minimum_size = Vector2(432, 240) +script = ExtResource("2_tsvun") +update_content = true +text = "Level complete!" +close_button_text = "Continue" +title_visible = false + +[node name="TitleMargin" parent="ContentContainer/BoxContainer" index="0"] +visible = false + +[node name="DescriptionLabel" parent="ContentContainer/BoxContainer/BodyMargin" index="0"] +text = "Level complete!" + +[node name="MenuButtons" parent="ContentContainer/BoxContainer/MenuButtonsMargin" index="0"] +vertical = false + +[node name="ExitButton" type="Button" parent="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="0"] +unique_name_in_owner = true +visible = false +layout_mode = 2 +size_flags_horizontal = 3 +text = "Exit" + +[node name="MainMenuButton" type="Button" parent="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="1"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +text = "Main Menu" + +[node name="RestartButton" type="Button" parent="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="2"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +text = "Restart" + +[node name="CloseButton" parent="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="3"] +size_flags_horizontal = 3 +text = "Continue" + +[node name="MainMenuConfirmation" parent="." index="1" instance=ExtResource("3_kw6pv")] +unique_name_in_owner = true +visible = false +custom_minimum_size = Vector2(220, 0) +layout_mode = 2 +text = "Exit to the main menu?" +title = "Confirm Exit" + +[node name="ExitConfirmation" parent="." index="2" instance=ExtResource("3_kw6pv")] +unique_name_in_owner = true +visible = false +layout_mode = 2 +text = "Exit the game?" +title = "Confirm Exit" + +[connection signal="pressed" from="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons/ExitButton" to="." method="_on_exit_button_pressed"] +[connection signal="pressed" from="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons/MainMenuButton" to="." method="_on_main_menu_button_pressed"] +[connection signal="pressed" from="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons/RestartButton" to="." method="_on_restart_button_pressed"] +[connection signal="confirmed" from="MainMenuConfirmation" to="." method="_on_main_menu_confirmation_confirmed"] +[connection signal="confirmed" from="ExitConfirmation" to="." method="_on_exit_confirmation_confirmed"] diff --git a/menus/scenes/windows/main_menu_credits_window.gd b/menus/scenes/windows/main_menu_credits_window.gd new file mode 100644 index 0000000..4673b4e --- /dev/null +++ b/menus/scenes/windows/main_menu_credits_window.gd @@ -0,0 +1,7 @@ +@tool +extends "res://addons/maaacks_game_template/base/nodes/windows/overlaid_window_scene_container.gd" + +func _ready() -> void: + super._ready() + if instance and instance.has_signal(&"end_reached"): + instance.connect(&"end_reached", close) diff --git a/menus/scenes/windows/main_menu_credits_window.gd.uid b/menus/scenes/windows/main_menu_credits_window.gd.uid new file mode 100644 index 0000000..3b348ea --- /dev/null +++ b/menus/scenes/windows/main_menu_credits_window.gd.uid @@ -0,0 +1 @@ +uid://c1apsdpynl7ex diff --git a/menus/scenes/windows/main_menu_credits_window.tscn b/menus/scenes/windows/main_menu_credits_window.tscn new file mode 100644 index 0000000..1e553a6 --- /dev/null +++ b/menus/scenes/windows/main_menu_credits_window.tscn @@ -0,0 +1,25 @@ +[gd_scene format=3 uid="uid://cvweffl7bo7bj"] + +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/windows/overlaid_window_scene_container.tscn" id="1_nuhlj"] +[ext_resource type="Script" path="res://menus/scenes/windows/main_menu_credits_window.gd" id="2_o2qx8"] +[ext_resource type="PackedScene" path="res://menus/scenes/credits/scrollable_credits.tscn" id="3_h2dw7"] + +[node name="MainMenuCreditsOverlaidWindow" instance=ExtResource("1_nuhlj")] +anchors_preset = 15 +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 0.0 +offset_top = 0.0 +offset_right = 0.0 +offset_bottom = 0.0 +script = ExtResource("2_o2qx8") +packed_scene = ExtResource("3_h2dw7") + +[node name="TitleMargin" parent="ContentContainer/BoxContainer" index="0"] +visible = false + +[node name="CloseButton" parent="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="0"] +size_flags_horizontal = 0 +text = "Back" diff --git a/menus/scenes/windows/main_menu_options_window.tscn b/menus/scenes/windows/main_menu_options_window.tscn new file mode 100644 index 0000000..fd8e177 --- /dev/null +++ b/menus/scenes/windows/main_menu_options_window.tscn @@ -0,0 +1,28 @@ +[gd_scene format=3 uid="uid://xkqu58vkydps"] + +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/windows/overlaid_window_scene_container.tscn" id="1_nkqug"] +[ext_resource type="PackedScene" path="res://menus/scenes/menus/options_menu/master_options_menu_with_tabs.tscn" id="2_ldm64"] + +[node name="MainMenuOptionsOverlaidWindow" instance=ExtResource("1_nkqug")] +anchors_preset = 15 +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 0.0 +offset_top = 0.0 +offset_right = 0.0 +offset_bottom = 0.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +packed_scene = ExtResource("2_ldm64") +update_content = true +close_button_text = "Back" +title_visible = false + +[node name="TitleMargin" parent="ContentContainer/BoxContainer" index="0"] +visible = false + +[node name="CloseButton" parent="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="0"] +size_flags_horizontal = 0 +text = "Back" diff --git a/menus/scenes/windows/pause_menu.gd b/menus/scenes/windows/pause_menu.gd new file mode 100644 index 0000000..f64fdb1 --- /dev/null +++ b/menus/scenes/windows/pause_menu.gd @@ -0,0 +1,119 @@ +@tool +extends OverlaidWindow + +@export var options_menu_scene : PackedScene +## Path to a main menu scene. +@export_file("*.tscn") var main_menu_scene_path : String +@export_node_path(&"ConfirmationOverlaidWindow") var restart_confirmation_node_path : NodePath +@export_node_path(&"ConfirmationOverlaidWindow") var main_menu_confirmation_node_path : NodePath +@export_node_path(&"ConfirmationOverlaidWindow") var exit_confirmation_node_path : NodePath +@export var menu_container_node_path : NodePath = ^".." + +@onready var restart_confirmation : ConfirmationOverlaidWindow = get_node(restart_confirmation_node_path) +@onready var main_menu_confirmation : ConfirmationOverlaidWindow = get_node(main_menu_confirmation_node_path) +@onready var exit_confirmation : ConfirmationOverlaidWindow = get_node(exit_confirmation_node_path) +@onready var menu_container : Node = get_node(menu_container_node_path) +@onready var options_button = %OptionsButton +@onready var main_menu_button = %MainMenuButton +@onready var exit_button = %ExitButton +## If Maaack's Scene Loader is installed, then it will be used to change scenes. +@onready var scene_loader_node = get_tree().root.get_node_or_null(^"SceneLoader") + +var open_window : Node +var _ignore_first_cancel : bool = false + +func get_main_menu_scene_path() -> String: + return main_menu_scene_path + +func close_window() -> void: + if open_window != null: + if open_window.has_method("close"): + open_window.close() + else: + open_window.hide() + open_window = null + +func _disable_focus() -> void: + for child in %MenuButtons.get_children(): + if child is Control: + child.focus_mode = FOCUS_NONE + +func _enable_focus() -> void: + for child in %MenuButtons.get_children(): + if child is Control: + child.focus_mode = FOCUS_ALL + +func _load_scene(scene_path: String) -> void: + _scene_tree.paused = false + if scene_loader_node: + scene_loader_node.load_scene(scene_path) + else: + get_tree().change_scene_to_file(scene_path) + +func _show_window(window : Control) -> void: + _disable_focus.call_deferred() + window.show() + open_window = window + await window.hidden + open_window = null + _enable_focus.call_deferred() + +func _load_and_show_menu(scene : PackedScene) -> void: + var window_instance : Control = scene.instantiate() + window_instance.visible = false + menu_container.add_child.call_deferred(window_instance) + await _show_window(window_instance) + window_instance.queue_free() + +func _handle_cancel_input() -> void: + if _ignore_first_cancel: + _ignore_first_cancel = false + return + if open_window != null: + close_window() + else: + super._handle_cancel_input() + +func show() -> void: + super.show() + if Input.is_action_pressed("ui_cancel"): + _ignore_first_cancel = true + +func _refresh_exit_button() -> void: + exit_button.visible = !OS.has_feature("web") + +func _refresh_options_button() -> void: + options_button.visible = options_menu_scene != null + +func _refresh_main_menu_button() -> void: + main_menu_button.visible = !get_main_menu_scene_path().is_empty() + +func _ready() -> void: + _refresh_exit_button() + _refresh_options_button() + _refresh_main_menu_button() + restart_confirmation.confirmed.connect(_on_restart_confirmation_confirmed) + main_menu_confirmation.confirmed.connect(_on_main_menu_confirmation_confirmed) + exit_confirmation.confirmed.connect(_on_exit_confirmation_confirmed) + +func _on_restart_button_pressed() -> void: + _show_window(restart_confirmation) + +func _on_options_button_pressed() -> void: + _load_and_show_menu(options_menu_scene) + +func _on_main_menu_button_pressed() -> void: + _show_window(main_menu_confirmation) + +func _on_exit_button_pressed() -> void: + _show_window(exit_confirmation) + +func _on_restart_confirmation_confirmed() -> void: + get_tree().reload_current_scene() + close() + +func _on_main_menu_confirmation_confirmed(): + _load_scene(get_main_menu_scene_path()) + +func _on_exit_confirmation_confirmed(): + get_tree().quit() diff --git a/menus/scenes/windows/pause_menu.gd.uid b/menus/scenes/windows/pause_menu.gd.uid new file mode 100644 index 0000000..7b198fa --- /dev/null +++ b/menus/scenes/windows/pause_menu.gd.uid @@ -0,0 +1 @@ +uid://bgajacd4x5y5c diff --git a/menus/scenes/windows/pause_menu.tscn b/menus/scenes/windows/pause_menu.tscn new file mode 100644 index 0000000..e04aaf4 --- /dev/null +++ b/menus/scenes/windows/pause_menu.tscn @@ -0,0 +1,82 @@ +[gd_scene format=3 uid="uid://docl4rn3h2wf8"] + +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/windows/overlaid_window.tscn" id="1_lqur3"] +[ext_resource type="Script" path="res://menus/scenes/windows/pause_menu.gd" id="2_h3gat"] +[ext_resource type="PackedScene" path="res://menus/scenes/windows/pause_menu_options_window.tscn" id="3_qnib3"] +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/windows/confirmation_overlaid_window.tscn" id="4_25tgp"] + +[node name="PauseMenu" instance=ExtResource("1_lqur3")] +process_mode = 3 +custom_minimum_size = Vector2(256, 312) +offset_left = -128.0 +offset_top = -155.0 +offset_right = 128.0 +offset_bottom = 157.0 +size_flags_horizontal = 1 +size_flags_vertical = 1 +script = ExtResource("2_h3gat") +options_menu_scene = ExtResource("3_qnib3") +main_menu_scene_path = "res://menus/scenes/menus/main_menu/main_menu_with_animations.tscn" +restart_confirmation_node_path = NodePath("RestartConfirmation") +main_menu_confirmation_node_path = NodePath("MainMenuConfirmation") +exit_confirmation_node_path = NodePath("ExitConfirmation") +menu_container_node_path = NodePath("..") +pauses_game = true + +[node name="TitleLabel" parent="ContentContainer/BoxContainer/TitleMargin/BoxContainer" index="0"] +text = "Paused" + +[node name="BodyMargin" parent="ContentContainer/BoxContainer" index="1"] +visible = false + +[node name="MenuButtonsMargin" parent="ContentContainer/BoxContainer" index="2"] +size_flags_vertical = 3 + +[node name="CloseButton" parent="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="0"] +text = "Resume" + +[node name="RestartButton" type="Button" parent="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="1"] +layout_mode = 2 +text = "Restart" + +[node name="OptionsButton" type="Button" parent="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="2"] +unique_name_in_owner = true +layout_mode = 2 +text = "Options" + +[node name="MainMenuButton" type="Button" parent="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="3"] +unique_name_in_owner = true +layout_mode = 2 +text = "Main Menu" + +[node name="ExitButton" type="Button" parent="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="4"] +unique_name_in_owner = true +layout_mode = 2 +text = "Exit Game" + +[node name="RestartConfirmation" parent="." index="1" instance=ExtResource("4_25tgp")] +unique_name_in_owner = true +visible = false +layout_mode = 2 +text = "Restart the game?" +title = "Confirm Restart" + +[node name="MainMenuConfirmation" parent="." index="2" instance=ExtResource("4_25tgp")] +unique_name_in_owner = true +visible = false +custom_minimum_size = Vector2(220, 0) +layout_mode = 2 +text = "Exit to the main menu?" +title = "Confirm Exit" + +[node name="ExitConfirmation" parent="." index="3" instance=ExtResource("4_25tgp")] +unique_name_in_owner = true +visible = false +layout_mode = 2 +text = "Exit the game?" +title = "Confirm Exit" + +[connection signal="pressed" from="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons/RestartButton" to="." method="_on_restart_button_pressed"] +[connection signal="pressed" from="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons/OptionsButton" to="." method="_on_options_button_pressed"] +[connection signal="pressed" from="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons/MainMenuButton" to="." method="_on_main_menu_button_pressed"] +[connection signal="pressed" from="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons/ExitButton" to="." method="_on_exit_button_pressed"] diff --git a/menus/scenes/windows/pause_menu_layer.gd b/menus/scenes/windows/pause_menu_layer.gd new file mode 100644 index 0000000..9ca9446 --- /dev/null +++ b/menus/scenes/windows/pause_menu_layer.gd @@ -0,0 +1,13 @@ +extends CanvasLayer + +@onready var pause_menu = %PauseMenu + +func _on_pause_menu_hidden(): + hide() + +func _on_visibility_changed(): + if visible: + pause_menu.show() + +func _ready(): + visibility_changed.connect(_on_visibility_changed) diff --git a/menus/scenes/windows/pause_menu_layer.gd.uid b/menus/scenes/windows/pause_menu_layer.gd.uid new file mode 100644 index 0000000..deddea1 --- /dev/null +++ b/menus/scenes/windows/pause_menu_layer.gd.uid @@ -0,0 +1 @@ +uid://bo7kdymgdhkdm diff --git a/menus/scenes/windows/pause_menu_layer.tscn b/menus/scenes/windows/pause_menu_layer.tscn new file mode 100644 index 0000000..955cb59 --- /dev/null +++ b/menus/scenes/windows/pause_menu_layer.tscn @@ -0,0 +1,98 @@ +[gd_scene format=3 uid="uid://w2f00usvsami"] + +[ext_resource type="Script" path="res://menus/scenes/windows/pause_menu_layer.gd" id="1_c8aok"] +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/windows/overlaid_window.tscn" id="2_a2qjc"] +[ext_resource type="Script" path="res://menus/scenes/windows/pause_menu.gd" id="3_xji41"] +[ext_resource type="PackedScene" path="res://menus/scenes/windows/pause_menu_options_window.tscn" id="4_tf3kx"] +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/windows/confirmation_overlaid_window.tscn" id="5_rhdwm"] + +[node name="PauseMenuLayer" type="CanvasLayer"] +process_mode = 3 +script = ExtResource("1_c8aok") + +[node name="PauseMenu" parent="." instance=ExtResource("2_a2qjc")] +unique_name_in_owner = true +process_mode = 3 +custom_minimum_size = Vector2(256, 312) +script = ExtResource("3_xji41") +options_menu_scene = ExtResource("4_tf3kx") +main_menu_scene_path = "res://menus/scenes/menus/main_menu/main_menu_with_animations.tscn" +restart_confirmation_node_path = NodePath("../RestartConfirmation") +main_menu_confirmation_node_path = NodePath("../MainMenuConfirmation") +exit_confirmation_node_path = NodePath("../ExitConfirmation") +menu_container_node_path = NodePath("..") +pauses_game = true +update_content = true +title = "Paused" + +[node name="TitleLabel" parent="PauseMenu/ContentContainer/BoxContainer/TitleMargin/BoxContainer" index="0"] +text = "Paused" + +[node name="BodyMargin" parent="PauseMenu/ContentContainer/BoxContainer" index="1"] +visible = false + +[node name="MenuButtonsMargin" parent="PauseMenu/ContentContainer/BoxContainer" index="2"] +size_flags_vertical = 3 + +[node name="CloseButton" parent="PauseMenu/ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="0"] +text = "Resume" + +[node name="RestartButton" type="Button" parent="PauseMenu/ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="1"] +layout_mode = 2 +text = "Restart" + +[node name="SaveGameButton" type="Button" parent="PauseMenu/ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="2"] +unique_name_in_owner = true +visible = false +layout_mode = 2 +text = "Save Game" + +[node name="LoadGameButton" type="Button" parent="PauseMenu/ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="3"] +unique_name_in_owner = true +visible = false +layout_mode = 2 +text = "Load Game" + +[node name="OptionsButton" type="Button" parent="PauseMenu/ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="4"] +unique_name_in_owner = true +layout_mode = 2 +text = "Options" + +[node name="MainMenuButton" type="Button" parent="PauseMenu/ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="5"] +unique_name_in_owner = true +layout_mode = 2 +text = "Main Menu" + +[node name="ExitButton" type="Button" parent="PauseMenu/ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="6"] +unique_name_in_owner = true +layout_mode = 2 +text = "Exit Game" + +[node name="RestartConfirmation" parent="." instance=ExtResource("5_rhdwm")] +unique_name_in_owner = true +visible = false +text = "Restart the game?" +title = "Confirm Restart" + +[node name="MainMenuConfirmation" parent="." instance=ExtResource("5_rhdwm")] +unique_name_in_owner = true +visible = false +custom_minimum_size = Vector2(220, 0) +text = "Exit to the main menu?" +title = "Confirm Exit" + +[node name="ExitConfirmation" parent="." instance=ExtResource("5_rhdwm")] +unique_name_in_owner = true +visible = false +text = "Exit the game?" +title = "Confirm Exit" + +[connection signal="hidden" from="PauseMenu" to="." method="_on_pause_menu_hidden"] +[connection signal="pressed" from="PauseMenu/ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons/RestartButton" to="PauseMenu" method="_on_restart_button_pressed"] +[connection signal="pressed" from="PauseMenu/ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons/SaveGameButton" to="PauseMenu" method="_on_save_game_button_pressed"] +[connection signal="pressed" from="PauseMenu/ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons/LoadGameButton" to="PauseMenu" method="_on_load_game_button_pressed"] +[connection signal="pressed" from="PauseMenu/ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons/OptionsButton" to="PauseMenu" method="_on_options_button_pressed"] +[connection signal="pressed" from="PauseMenu/ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons/MainMenuButton" to="PauseMenu" method="_on_main_menu_button_pressed"] +[connection signal="pressed" from="PauseMenu/ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons/ExitButton" to="PauseMenu" method="_on_exit_button_pressed"] + +[editable path="PauseMenu"] diff --git a/menus/scenes/windows/pause_menu_options_window.tscn b/menus/scenes/windows/pause_menu_options_window.tscn new file mode 100644 index 0000000..009c38d --- /dev/null +++ b/menus/scenes/windows/pause_menu_options_window.tscn @@ -0,0 +1,28 @@ +[gd_scene format=3 uid="uid://tbhmg0d0ns6i"] + +[ext_resource type="PackedScene" path="res://addons/maaacks_game_template/base/nodes/windows/overlaid_window_scene_container.tscn" id="1_fimhq"] +[ext_resource type="PackedScene" path="res://menus/scenes/menus/options_menu/master_options_menu_with_tabs.tscn" id="2_j26ns"] + +[node name="PauseMenuOptionsOverlaidWindow" instance=ExtResource("1_fimhq")] +process_mode = 3 +anchors_preset = 15 +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 0.0 +offset_top = 0.0 +offset_right = 0.0 +offset_bottom = 0.0 +packed_scene = ExtResource("2_j26ns") +title_visible = false + +[node name="TitleMargin" parent="ContentContainer/BoxContainer" index="0"] +visible = false + +[node name="TitleLabel" parent="ContentContainer/BoxContainer/TitleMargin/BoxContainer" index="0"] +text = "Options" + +[node name="CloseButton" parent="ContentContainer/BoxContainer/MenuButtonsMargin/MenuButtons" index="0"] +size_flags_horizontal = 0 +text = "Back" diff --git a/override.cfg b/override.cfg new file mode 100644 index 0000000..8ca7535 --- /dev/null +++ b/override.cfg @@ -0,0 +1,36 @@ +; Project settings override file. +; Adds gamepad inputs to built-in actions. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + + +[input] + +ui_accept={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194309,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194310,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":32,"physical_keycode":0,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":0,"pressure":0.0,"pressed":true,"script":null) +] +} +ui_cancel={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194305,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":6,"pressure":0.0,"pressed":true,"script":null) +] +} +ui_page_up={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194323,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":9,"pressure":0.0,"pressed":true,"script":null) +] +} +ui_page_down={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194324,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":10,"pressure":0.0,"pressed":true,"script":null) +] +} diff --git a/project.godot b/project.godot index 30f0cd0..6362c02 100644 --- a/project.godot +++ b/project.godot @@ -11,15 +11,38 @@ config_version=5 [application] config/name="GGJ26" -run/main_scene="uid://cukfdjcnb5tm6" +run/main_scene="res://menus/scenes/opening/opening.tscn" config/features=PackedStringArray("4.6", "Forward Plus") config/icon="res://icon.svg" +[audio] + +buses/default_bus_layout="uid://ch8r6u24fgg03" + [display] window/size/viewport_width=1920 window/size/viewport_height=1080 +[editor_plugins] + +enabled=PackedStringArray("res://addons/maaacks_game_template/plugin.cfg") + +[gui] + +theme/custom="res://menus/resources/themes/grow.tres" + +[internationalization] + +locale/translations=PackedStringArray("res://addons/maaacks_game_template/base/translations/menus_translations.en.translation", "res://addons/maaacks_game_template/base/translations/menus_translations.fr.translation") + +[maaacks_game_template] + +disable_install_audio_busses=true +disable_update_check=false +disable_install_wizard=true +copy_path="res://menus/" + [physics] 3d/physics_engine="Jolt Physics" diff --git a/main.tscn b/scenes/main.tscn similarity index 100% rename from main.tscn rename to scenes/main.tscn