├── .gitattributes ├── .github └── workflows │ └── godot-ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── docs └── custom-theme.png ├── godot ├── default_bus_layout.tres ├── export_presets.cfg ├── fonts │ ├── Montserrat-Medium.ttf │ └── Montserrat-Medium.ttf.import ├── i18n │ ├── translation.csv │ ├── translation.csv.import │ ├── translation.de.translation │ └── translation.en.translation ├── icon.svg ├── icon.svg.import ├── project.godot ├── savegame │ ├── save_game.gd │ └── save_game.gd.uid ├── scenes │ ├── boot │ │ ├── bootsplash_scene.gd │ │ ├── bootsplash_scene.gd.uid │ │ ├── bootsplash_scene.tscn │ │ ├── godot │ │ │ ├── godot_bootsplash_scene.tscn │ │ │ └── godot_logo.tscn │ │ ├── logo.png │ │ └── logo.png.import │ ├── game_settings_scene.gd │ ├── game_settings_scene.gd.uid │ ├── game_settings_scene.tscn │ ├── ingame_scene.gd │ ├── ingame_scene.gd.uid │ ├── ingame_scene.tscn │ ├── intro_scene.tscn │ ├── main_menu_scene.gd │ ├── main_menu_scene.gd.uid │ ├── main_menu_scene.tscn │ ├── node_example.gd │ └── node_example.gd.uid ├── settings │ ├── user_settings.gd │ └── user_settings.gd.uid └── ui │ ├── components │ ├── bootsplash.gd │ ├── bootsplash.gd.uid │ ├── bootsplash.tscn │ ├── float_range_game_settings_option.gd │ ├── float_range_game_settings_option.gd.uid │ ├── float_range_game_settings_option.tscn │ ├── game_logo.tscn │ ├── game_settings.gd │ ├── game_settings.gd.uid │ └── game_settings.tscn │ ├── overlays │ ├── fade_overlay.gd │ ├── fade_overlay.gd.uid │ ├── fade_overlay.tscn │ ├── pause_overlay.gd │ ├── pause_overlay.gd.uid │ └── pause_overlay.tscn │ └── theme.tres └── logo.png /.gitattributes: -------------------------------------------------------------------------------- 1 | # Normalize EOL for all files that Git considers text files. 2 | * text=auto eol=lf 3 | -------------------------------------------------------------------------------- /.github/workflows/godot-ci.yml: -------------------------------------------------------------------------------- 1 | name: Godot CI/CD Pipeline 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | GODOT_VERSION: "4.2.1" 7 | GODOT_STATUS: "stable" 8 | jobs: 9 | import-assets: 10 | runs-on: ubuntu-latest 11 | container: barichello/godot-ci:4.2.1 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Cache import assets 15 | uses: actions/cache@v3 16 | with: 17 | path: .godot/imported/ 18 | key: import-assets-${{ runner.os }}-${{ github.sha }} 19 | - name: Import assets 20 | run: godot --headless --verbose --editor --quit 21 | 22 | export-game: 23 | runs-on: ubuntu-latest 24 | needs: import-assets 25 | steps: 26 | - uses: actions/checkout@v4 27 | - uses: actions/cache@v3 28 | with: 29 | path: .godot/imported/ 30 | key: import-assets-${{ runner.os }}-${{ github.sha }} 31 | - name: install wine 32 | run: | 33 | sudo apt install wine64 34 | echo "WINE_PATH=$(which wine64)" >> $GITHUB_ENV 35 | - name: Export Game using Godot 36 | uses: firebelley/godot-export@v5.2.1 37 | with: 38 | godot_executable_download_url: "https://github.com/godotengine/godot/releases/download/${{ env.GODOT_VERSION }}-${{ env.GODOT_STATUS }}/Godot_v${{ env.GODOT_VERSION }}-${{ env.GODOT_STATUS }}_linux.x86_64.zip" 39 | godot_export_templates_download_url: "https://github.com/godotengine/godot/releases/download/${{ env.GODOT_VERSION }}-${{ env.GODOT_STATUS }}/Godot_v${{ env.GODOT_VERSION }}-${{ env.GODOT_STATUS }}_export_templates.tpz" 40 | relative_project_path: "./godot" 41 | archive_output: true 42 | cache: false 43 | wine_path: ${{ env.WINE_PATH }} 44 | - name: Upload HTML5 45 | uses: actions/upload-artifact@v4 46 | with: 47 | name: HTML5 48 | path: /home/runner/.local/share/godot/archives/HTML5.zip 49 | - name: Upload Linux 50 | uses: actions/upload-artifact@v4 51 | with: 52 | name: LinuxX11 53 | path: /home/runner/.local/share/godot/archives/LinuxX11.zip 54 | - name: Upload Windows 55 | uses: actions/upload-artifact@v4 56 | with: 57 | name: Windows 58 | path: /home/runner/.local/share/godot/archives/Windows.zip 59 | - name: Upload MacOS 60 | uses: actions/upload-artifact@v4 61 | with: 62 | name: macOS 63 | path: /home/runner/.local/share/godot/archives/macOS.zip 64 | 65 | deploy-to-itchio-linux: 66 | runs-on: ubuntu-latest 67 | needs: export-game 68 | steps: 69 | - uses: actions/checkout@v4 70 | - uses: actions/download-artifact@v4 71 | with: 72 | name: LinuxX11 73 | - name: Deploy to Itch.io (Windows) 74 | uses: manleydev/butler-publish-itchio-action@master 75 | env: 76 | BUTLER_CREDENTIALS: ${{ secrets.BUTLER_API_KEY }} 77 | CHANNEL: linux 78 | ITCH_GAME: ${{ secrets.ITCHIO_GAME }} 79 | ITCH_USER: ${{ secrets.ITCHIO_USERNAME }} 80 | PACKAGE: LinuxX11.zip 81 | 82 | deploy-to-itchio-windows: 83 | runs-on: ubuntu-latest 84 | needs: export-game 85 | steps: 86 | - uses: actions/checkout@v4 87 | - uses: actions/download-artifact@v4 88 | with: 89 | name: Windows 90 | - name: Deploy to Itch.io (Windows) 91 | uses: manleydev/butler-publish-itchio-action@master 92 | env: 93 | BUTLER_CREDENTIALS: ${{ secrets.BUTLER_API_KEY }} 94 | CHANNEL: windows 95 | ITCH_GAME: ${{ secrets.ITCHIO_GAME }} 96 | ITCH_USER: ${{ secrets.ITCHIO_USERNAME }} 97 | PACKAGE: Windows.zip 98 | 99 | deploy-to-itchio-mac: 100 | runs-on: ubuntu-latest 101 | needs: export-game 102 | steps: 103 | - uses: actions/checkout@v4 104 | - uses: actions/download-artifact@v4 105 | with: 106 | name: macOS 107 | - name: Deploy to Itch.io (MacOS) 108 | uses: manleydev/butler-publish-itchio-action@master 109 | env: 110 | BUTLER_CREDENTIALS: ${{ secrets.BUTLER_API_KEY }} 111 | CHANNEL: mac 112 | ITCH_GAME: ${{ secrets.ITCHIO_GAME }} 113 | ITCH_USER: ${{ secrets.ITCHIO_USERNAME }} 114 | PACKAGE: macOS.zip 115 | 116 | deploy-to-itchio-web: 117 | runs-on: ubuntu-latest 118 | needs: export-game 119 | steps: 120 | - uses: actions/checkout@v4 121 | - uses: actions/download-artifact@v4 122 | with: 123 | name: HTML5 124 | - name: Deploy to Itch.io (HTML5) 125 | uses: manleydev/butler-publish-itchio-action@master 126 | env: 127 | BUTLER_CREDENTIALS: ${{ secrets.BUTLER_API_KEY }} 128 | CHANNEL: web 129 | ITCH_GAME: ${{ secrets.ITCHIO_GAME }} 130 | ITCH_USER: ${{ secrets.ITCHIO_USERNAME }} 131 | PACKAGE: HTML5.zip 132 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Referenced from: https://github.com/github/gitignore/blob/main/Godot.gitignore 2 | # Godot 4+ specific ignores 3 | .godot/ 4 | 5 | # Godot-specific ignores 6 | .import/ 7 | export.cfg 8 | export_presets.cfg 9 | 10 | # Imported translations (automatically generated from CSV files) 11 | *.translation 12 | 13 | # Mono-specific ignores 14 | .mono/ 15 | data_*/ 16 | mono_crash.*.json 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 miguel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![godot-gamejam](logo.png) 2 | 3 | **🤖 Godot Engine 4.0 template to better get started for gamejams with your game!** 4 | 5 | ![](https://img.shields.io/badge/Godot%20Compatible-4.0%2B-%234385B5) [![](https://img.shields.io/discord/785246324793540608.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.com/invite/CKBuE5djXe) 6 | 7 | # 🧪 Features 8 | 9 | ### 🌌 **Game Settings** 10 | 11 | This template project comes with game settings out of the box: 12 | 13 | - **Audio volume** (Master, Sound, Music) 14 | - **Game Language** (English only by default) 15 | 16 | ### ✨ **Bootsplash scene support** 17 | 18 | Either use the default Godot bootsplash or define your own logo bootsplashes! 19 | 20 | ### 🎩 Basic menu flow (main menu, settings, pause) 21 | 22 | This project comes with basic menu navigation that allows you to navigate between ingame, settings and quitting the game. 23 | 24 | ### 💾 Save Game Integration 25 | 26 | Automatically saves the state of the game on exit. Starting a new game overrides the existing save game. Any game object that needs to be saved has to be part of the "Persist" group. 27 | 28 | ### 🌎 Translation support (i18n) 29 | 30 | Comes with pre-defined translations that can be extended. 31 | 32 | ### ⏸ Pause Menu 33 | 34 | The `IngameScene` supports a pause menu to temporarily pause the game. 35 | 36 | ### _🚀 Automatic [itch.io](https://itch.io) deployment_ 37 | 38 | This templates comes with automatic deployment to itch.io for MacOS, Windows, Linux and HTML5 (Web). Delete `.github/workflows/godot-ci.yml` in case you are not interested in deploying to itch.io! 39 | 40 | 1. generate new API key in your itch.io settings and setup your `BUTLER_API_KEY` secret - [learn more](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions) 41 | 2. create a new game on itch.io 42 | 3. setup `ITCHIO_GAME` and `ITCHIO_USERNAME` secrets in Github and assign them to your itch.io username and game name 43 | 44 | [See working example here](https://github.com/bitbrain/ggj-2023/blob/main/.github/workflows/godot-ci.yml) 45 | 46 | # 🎮 Getting started! 47 | 48 | 1. Click `Use this template` in Github ([learn more about Template Repositories](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-repository-from-a-template)) 49 | 2. Open the project in Godot Engine 4 50 | 3. Customize it to your needs (see [🧱 Customization](#-customization)) 51 | 4. Open the `IngameScene` and start building your game! 52 | 53 | # 🔌 Compatible Gamejams 54 | 55 | > **Not all gamejams allow project templates!** Please make sure to carefully read the gamejam rules before using this project, otherwise you might **risk being disqualified**! 56 | 57 | Down below is a list of some compatible gamejam formats that permit using this repository as a template: 58 | 59 | - [GoGodotJam](https://gogodotjam.com/the-jam/) 60 | - [Ludum Dare](https://ldjam.com/events/ludum-dare/rules) 61 | - [GMTK Game Jam](https://itch.io/jam/gmtk-jam-2022) 62 | - [Brackeys Gamejam](https://itch.io/jam/brackeys-7) 63 | - [Global Game Jam](https://globalgamejam.org/news/be-cool-rules) 64 | - [GameDevLondon Summer Jam](https://itch.io/jam/game-dev-london-summer-jam-2021) 65 | - [Godot Wild Jam](https://itch.io/jam/godot-wild-jam-49) 66 | 67 | # 🧱 Customization 68 | 69 | This project is built to be as generic as possible and it can be fully adjusted to your needs. 70 | 71 | ## Defining custom UI theme 72 | 73 | Search for the file `theme.tres` and open it. The [Godot theme editor](https://docs.godotengine.org/en/stable/tutorials/ui/gui_using_theme_editor.html) should appear. Feel free to customise the UI to your needs. Any `Control` node will use this theme by default: 74 | 75 | ![custom-theme](docs/custom-theme.png) 76 | 77 | ## Choosing 2D vs 3D 78 | 79 | This toolkit is not limited to either 2D or 3D. It has been kept vague on purpose so feel free to build 2D or 3D scenes with it! 80 | 81 | ## Custom boot splash 82 | 83 | Within Godot Engine, head to `scenes/boot` to access boot splash scenes. You can create your custom bootsplash by either modifying `GodotBootsplashScene` or creating a new node of type `Control`. Add `BootsplashScene` as a child and configure its properties as follows: 84 | 85 | - `Fade Duration` - the amount of time in milliseconds to fade in/out the bootsplash. 86 | - `Stay Duration` - the amount of time in milliseconds the bootsplash should stay. 87 | - `Node` - the node/scene that should be displayed during the bootsplash. 88 | - `Next Scene` - the next scene that should be loaded after the bootsplash has finished. 89 | - `Interruptable ` - wether the bootsplash is interruptable or not. 90 | 91 | ## Add new translation 92 | 93 | You will find a translation file in `i18n/translation.csv`. Feel free to extend it to your needs. Godot will automatically pickup the translations if you use them inside your control nodes like `Button` or `Label`. In case you need to manually translate something in a script, call the `tr()` function. 94 | 95 | In case you want to add a new language, add a new column to the `translation.csv` file. 96 | 97 | ## Game Settings 98 | 99 | The game settings are managed in `UserSettings.gd`. You can change the defaults by updating `USER_SETTING_DEFAULTS`. You can also add additional properties to extend them. 100 | 101 | The `GameSettings` node and the `GameSettings.gd` script connects to the `UserSettings` by setting it/loading from it. 102 | 103 | ## Save game configuration 104 | 105 | By default, save games are enabled. You can toggle support for save games inside `SaveGame.gd` by changing the `ENABLED` property. 106 | 107 | The save game saves any node inside the `IngameScene` that is part of the `Persist` group ([learn more about groups in Godot](https://docs.godotengine.org/en/stable/tutorials/scripting/groups.html)). The save game system will save existing nodes but also nodes that were dynamically added to the scene at runtime! 108 | 109 | Currently, the game will only save the scene when exiting the game via the pause menu. 110 | 111 | # 🍻 Contributing 112 | 113 | Feel free to raise a pull request or suggest any changes or improvements you would like to see! 114 | 115 | > 🐱‍💻 **PLEASE NOTE:** this project is a template and any contribution should only introduce/extend features that are template related. If something can be its own addon (e.g. movement controller or specific 2D/3D stuff) it probably should be its own (separate) addon. 116 | -------------------------------------------------------------------------------- /docs/custom-theme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitbrain/godot-gamejam/53baefb2348c4ed8a0e7d0211b26ec3097c4cf56/docs/custom-theme.png -------------------------------------------------------------------------------- /godot/default_bus_layout.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="AudioBusLayout" format=3 uid="uid://dq3dvojo8sb1n"] 2 | 3 | [resource] 4 | bus/1/name = &"Sound" 5 | bus/1/solo = false 6 | bus/1/mute = false 7 | bus/1/bypass_fx = false 8 | bus/1/volume_db = 0.0 9 | bus/1/send = &"Master" 10 | bus/2/name = &"Music" 11 | bus/2/solo = false 12 | bus/2/mute = false 13 | bus/2/bypass_fx = false 14 | bus/2/volume_db = 0.0 15 | bus/2/send = &"Master" 16 | -------------------------------------------------------------------------------- /godot/export_presets.cfg: -------------------------------------------------------------------------------- 1 | [preset.0] 2 | 3 | name="LinuxX11" 4 | platform="Linux/X11" 5 | runnable=true 6 | dedicated_server=false 7 | custom_features="" 8 | export_filter="all_resources" 9 | include_filter="" 10 | exclude_filter="" 11 | export_path="./game.x86_64" 12 | encryption_include_filters="" 13 | encryption_exclude_filters="" 14 | encrypt_pck=false 15 | encrypt_directory=false 16 | 17 | [preset.0.options] 18 | 19 | custom_template/debug="" 20 | custom_template/release="" 21 | debug/export_console_wrapper=1 22 | binary_format/embed_pck=false 23 | texture_format/bptc=true 24 | texture_format/s3tc=true 25 | texture_format/etc=false 26 | texture_format/etc2=false 27 | binary_format/architecture="x86_64" 28 | ssh_remote_deploy/enabled=false 29 | ssh_remote_deploy/host="user@host_ip" 30 | ssh_remote_deploy/port="22" 31 | ssh_remote_deploy/extra_args_ssh="" 32 | ssh_remote_deploy/extra_args_scp="" 33 | ssh_remote_deploy/run_script="#!/usr/bin/env bash 34 | export DISPLAY=:0 35 | unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\" 36 | \"{temp_dir}/{exe_name}\" {cmd_args}" 37 | ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash 38 | kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\") 39 | rm -rf \"{temp_dir}\"" 40 | 41 | [preset.1] 42 | 43 | name="Windows" 44 | platform="Windows Desktop" 45 | runnable=true 46 | dedicated_server=false 47 | custom_features="" 48 | export_filter="all_resources" 49 | include_filter="" 50 | exclude_filter="" 51 | export_path="./game.exe" 52 | encryption_include_filters="" 53 | encryption_exclude_filters="" 54 | encrypt_pck=false 55 | encrypt_directory=false 56 | 57 | [preset.1.options] 58 | 59 | custom_template/debug="" 60 | custom_template/release="" 61 | debug/export_console_wrapper=1 62 | binary_format/embed_pck=false 63 | texture_format/bptc=true 64 | texture_format/s3tc=true 65 | texture_format/etc=false 66 | texture_format/etc2=false 67 | binary_format/architecture="x86_64" 68 | codesign/enable=false 69 | codesign/timestamp=true 70 | codesign/timestamp_server_url="" 71 | codesign/digest_algorithm=1 72 | codesign/description="" 73 | codesign/custom_options=PackedStringArray() 74 | application/modify_resources=true 75 | application/icon="" 76 | application/console_wrapper_icon="" 77 | application/icon_interpolation=4 78 | application/file_version="" 79 | application/product_version="" 80 | application/company_name="" 81 | application/product_name="" 82 | application/file_description="" 83 | application/copyright="" 84 | application/trademarks="" 85 | application/export_angle=0 86 | ssh_remote_deploy/enabled=false 87 | ssh_remote_deploy/host="user@host_ip" 88 | ssh_remote_deploy/port="22" 89 | ssh_remote_deploy/extra_args_ssh="" 90 | ssh_remote_deploy/extra_args_scp="" 91 | ssh_remote_deploy/run_script="Expand-Archive -LiteralPath '{temp_dir}\\{archive_name}' -DestinationPath '{temp_dir}' 92 | $action = New-ScheduledTaskAction -Execute '{temp_dir}\\{exe_name}' -Argument '{cmd_args}' 93 | $trigger = New-ScheduledTaskTrigger -Once -At 00:00 94 | $settings = New-ScheduledTaskSettingsSet 95 | $task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $settings 96 | Register-ScheduledTask godot_remote_debug -InputObject $task -Force:$true 97 | Start-ScheduledTask -TaskName godot_remote_debug 98 | while (Get-ScheduledTask -TaskName godot_remote_debug | ? State -eq running) { Start-Sleep -Milliseconds 100 } 99 | Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue" 100 | ssh_remote_deploy/cleanup_script="Stop-ScheduledTask -TaskName godot_remote_debug -ErrorAction:SilentlyContinue 101 | Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue 102 | Remove-Item -Recurse -Force '{temp_dir}'" 103 | 104 | [preset.2] 105 | 106 | name="HTML5" 107 | platform="Web" 108 | runnable=true 109 | dedicated_server=false 110 | custom_features="" 111 | export_filter="all_resources" 112 | include_filter="" 113 | exclude_filter="" 114 | export_path="./index.html" 115 | encryption_include_filters="" 116 | encryption_exclude_filters="" 117 | encrypt_pck=false 118 | encrypt_directory=false 119 | 120 | [preset.2.options] 121 | 122 | custom_template/debug="" 123 | custom_template/release="" 124 | variant/extensions_support=false 125 | vram_texture_compression/for_desktop=true 126 | vram_texture_compression/for_mobile=false 127 | html/export_icon=true 128 | html/custom_html_shell="" 129 | html/head_include="" 130 | html/canvas_resize_policy=2 131 | html/focus_canvas_on_start=true 132 | html/experimental_virtual_keyboard=false 133 | progressive_web_app/enabled=false 134 | progressive_web_app/offline_page="" 135 | progressive_web_app/display=1 136 | progressive_web_app/orientation=0 137 | progressive_web_app/icon_144x144="" 138 | progressive_web_app/icon_180x180="" 139 | progressive_web_app/icon_512x512="" 140 | progressive_web_app/background_color=Color(0, 0, 0, 1) 141 | 142 | [preset.3] 143 | 144 | name="macOS" 145 | platform="macOS" 146 | runnable=true 147 | dedicated_server=false 148 | custom_features="" 149 | export_filter="all_resources" 150 | include_filter="" 151 | exclude_filter="" 152 | export_path="./game.zip" 153 | encryption_include_filters="" 154 | encryption_exclude_filters="" 155 | encrypt_pck=false 156 | encrypt_directory=false 157 | 158 | [preset.3.options] 159 | 160 | export/distribution_type=1 161 | binary_format/architecture="universal" 162 | custom_template/debug="" 163 | custom_template/release="" 164 | debug/export_console_wrapper=1 165 | application/icon="" 166 | application/icon_interpolation=4 167 | application/bundle_identifier="org.godotengine" 168 | application/signature="" 169 | application/app_category="Games" 170 | application/short_version="" 171 | application/version="" 172 | application/copyright="" 173 | application/copyright_localized={} 174 | application/min_macos_version="10.12" 175 | application/export_angle=0 176 | display/high_res=true 177 | xcode/platform_build="14C18" 178 | xcode/sdk_version="13.1" 179 | xcode/sdk_build="22C55" 180 | xcode/sdk_name="macosx13.1" 181 | xcode/xcode_version="1420" 182 | xcode/xcode_build="14C18" 183 | codesign/codesign=1 184 | codesign/installer_identity="" 185 | codesign/apple_team_id="" 186 | codesign/identity="" 187 | codesign/entitlements/custom_file="" 188 | codesign/entitlements/allow_jit_code_execution=false 189 | codesign/entitlements/allow_unsigned_executable_memory=false 190 | codesign/entitlements/allow_dyld_environment_variables=false 191 | codesign/entitlements/disable_library_validation=false 192 | codesign/entitlements/audio_input=false 193 | codesign/entitlements/camera=false 194 | codesign/entitlements/location=false 195 | codesign/entitlements/address_book=false 196 | codesign/entitlements/calendars=false 197 | codesign/entitlements/photos_library=false 198 | codesign/entitlements/apple_events=false 199 | codesign/entitlements/debugging=false 200 | codesign/entitlements/app_sandbox/enabled=false 201 | codesign/entitlements/app_sandbox/network_server=false 202 | codesign/entitlements/app_sandbox/network_client=false 203 | codesign/entitlements/app_sandbox/device_usb=false 204 | codesign/entitlements/app_sandbox/device_bluetooth=false 205 | codesign/entitlements/app_sandbox/files_downloads=0 206 | codesign/entitlements/app_sandbox/files_pictures=0 207 | codesign/entitlements/app_sandbox/files_music=0 208 | codesign/entitlements/app_sandbox/files_movies=0 209 | codesign/entitlements/app_sandbox/files_user_selected=0 210 | codesign/entitlements/app_sandbox/helper_executables=[] 211 | codesign/custom_options=PackedStringArray() 212 | notarization/notarization=0 213 | privacy/microphone_usage_description="" 214 | privacy/microphone_usage_description_localized={} 215 | privacy/camera_usage_description="" 216 | privacy/camera_usage_description_localized={} 217 | privacy/location_usage_description="" 218 | privacy/location_usage_description_localized={} 219 | privacy/address_book_usage_description="" 220 | privacy/address_book_usage_description_localized={} 221 | privacy/calendar_usage_description="" 222 | privacy/calendar_usage_description_localized={} 223 | privacy/photos_library_usage_description="" 224 | privacy/photos_library_usage_description_localized={} 225 | privacy/desktop_folder_usage_description="" 226 | privacy/desktop_folder_usage_description_localized={} 227 | privacy/documents_folder_usage_description="" 228 | privacy/documents_folder_usage_description_localized={} 229 | privacy/downloads_folder_usage_description="" 230 | privacy/downloads_folder_usage_description_localized={} 231 | privacy/network_volumes_usage_description="" 232 | privacy/network_volumes_usage_description_localized={} 233 | privacy/removable_volumes_usage_description="" 234 | privacy/removable_volumes_usage_description_localized={} 235 | ssh_remote_deploy/enabled=false 236 | ssh_remote_deploy/host="user@host_ip" 237 | ssh_remote_deploy/port="22" 238 | ssh_remote_deploy/extra_args_ssh="" 239 | ssh_remote_deploy/extra_args_scp="" 240 | ssh_remote_deploy/run_script="#!/usr/bin/env bash 241 | unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\" 242 | open \"{temp_dir}/{exe_name}.app\" --args {cmd_args}" 243 | ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash 244 | kill $(pgrep -x -f \"{temp_dir}/{exe_name}.app/Contents/MacOS/{exe_name} {cmd_args}\") 245 | rm -rf \"{temp_dir}\"" 246 | -------------------------------------------------------------------------------- /godot/fonts/Montserrat-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitbrain/godot-gamejam/53baefb2348c4ed8a0e7d0211b26ec3097c4cf56/godot/fonts/Montserrat-Medium.ttf -------------------------------------------------------------------------------- /godot/fonts/Montserrat-Medium.ttf.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="font_data_dynamic" 4 | type="FontFile" 5 | uid="uid://le2vdo2626vw" 6 | path="res://.godot/imported/Montserrat-Medium.ttf-e832861e4ad4110e172112dc430c04b0.fontdata" 7 | 8 | [deps] 9 | 10 | source_file="res://fonts/Montserrat-Medium.ttf" 11 | dest_files=["res://.godot/imported/Montserrat-Medium.ttf-e832861e4ad4110e172112dc430c04b0.fontdata"] 12 | 13 | [params] 14 | 15 | Rendering=null 16 | antialiasing=1 17 | generate_mipmaps=false 18 | disable_embedded_bitmaps=true 19 | multichannel_signed_distance_field=false 20 | msdf_pixel_range=8 21 | msdf_size=48 22 | allow_system_fallback=true 23 | force_autohinter=false 24 | hinting=1 25 | subpixel_positioning=1 26 | keep_rounding_remainders=true 27 | oversampling=0.0 28 | Fallbacks=null 29 | fallbacks=[] 30 | Compress=null 31 | compress=true 32 | preload=[] 33 | language_support={} 34 | script_support={} 35 | opentype_features={} 36 | -------------------------------------------------------------------------------- /godot/i18n/translation.csv: -------------------------------------------------------------------------------- 1 | keys,en,de 2 | credits,Credits here,Credits hier 3 | new_game,New Game,Neues Spiel 4 | continue,Continue,Fortfahren 5 | settings,Game Settings,Einstellungen 6 | leave_game,Leave game,Spiel verlassen 7 | return_to_main,Return to main menu,Zum Hauptmenü 8 | return_to_menu,Back to menu,Zurück 9 | settings_volume_master_enabled,Master Enabled,Lautstärke an 10 | settings_volume_master,Master Volume,Lautstärke (Allgemein) 11 | settings_volume_music_enabled,Music enabled,Musik an 12 | settings_volume_music,Music Volume,Musik Lautstärke 13 | settings_volume_sound_enabled,Sound enabled,Sound an 14 | settings_volume_sound,Sound Volume,Sound Lautstärke 15 | settings_language,Game Language,Spielsprache 16 | game_paused,Game paused,Spiel pausiert 17 | resume_game,Resume game,Weiterspielen 18 | -------------------------------------------------------------------------------- /godot/i18n/translation.csv.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="csv_translation" 4 | type="Translation" 5 | uid="uid://ce8ocwmy5fhw6" 6 | 7 | [deps] 8 | 9 | files=["res://i18n/translation.en.translation", "res://i18n/translation.de.translation"] 10 | 11 | source_file="res://i18n/translation.csv" 12 | dest_files=["res://i18n/translation.en.translation", "res://i18n/translation.de.translation"] 13 | 14 | [params] 15 | 16 | compress=true 17 | delimiter=0 18 | -------------------------------------------------------------------------------- /godot/i18n/translation.de.translation: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitbrain/godot-gamejam/53baefb2348c4ed8a0e7d0211b26ec3097c4cf56/godot/i18n/translation.de.translation -------------------------------------------------------------------------------- /godot/i18n/translation.en.translation: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitbrain/godot-gamejam/53baefb2348c4ed8a0e7d0211b26ec3097c4cf56/godot/i18n/translation.en.translation -------------------------------------------------------------------------------- /godot/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /godot/icon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://dmelj2qgr8w7k" 6 | path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://icon.svg" 14 | dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /godot/project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="MyGame" 14 | run/main_scene="res://scenes/boot/godot/godot_bootsplash_scene.tscn" 15 | config/features=PackedStringArray("4.4") 16 | config/icon="res://icon.svg" 17 | 18 | [autoload] 19 | 20 | UserSettings="*res://settings/user_settings.gd" 21 | 22 | [display] 23 | 24 | window/size/viewport_width=576 25 | window/size/viewport_height=324 26 | window/size/window_width_override=1920 27 | window/size/window_height_override=1080 28 | window/stretch/mode="canvas_items" 29 | window/stretch/aspect="keep_height" 30 | 31 | [gui] 32 | 33 | theme/custom="res://ui/theme.tres" 34 | 35 | [input] 36 | 37 | ui_accept={ 38 | "deadzone": 0.5, 39 | "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":4194309,"location":0,"echo":false,"script":null) 40 | , 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":4194310,"location":0,"echo":false,"script":null) 41 | , 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) 42 | , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":0,"pressure":0.0,"pressed":false,"script":null) 43 | ] 44 | } 45 | exit={ 46 | "deadzone": 0.5, 47 | "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":0,"physical_keycode":4194305,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) 48 | ] 49 | } 50 | move_left={ 51 | "deadzone": 0.5, 52 | "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":0,"physical_keycode":65,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) 53 | , 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":0,"physical_keycode":4194319,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) 54 | , Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":-1.0,"script":null) 55 | ] 56 | } 57 | move_up={ 58 | "deadzone": 0.5, 59 | "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":0,"physical_keycode":87,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) 60 | , 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":0,"physical_keycode":4194320,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) 61 | , Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":-1.0,"script":null) 62 | ] 63 | } 64 | move_right={ 65 | "deadzone": 0.5, 66 | "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":0,"physical_keycode":68,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) 67 | , 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":0,"physical_keycode":4194321,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) 68 | , Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":1.0,"script":null) 69 | ] 70 | } 71 | move_down={ 72 | "deadzone": 0.5, 73 | "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":0,"physical_keycode":83,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) 74 | , 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":0,"physical_keycode":4194322,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) 75 | , Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":1.0,"script":null) 76 | ] 77 | } 78 | interact={ 79 | "deadzone": 0.5, 80 | "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":0,"physical_keycode":69,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) 81 | , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":0,"pressure":0.0,"pressed":false,"script":null) 82 | ] 83 | } 84 | pause={ 85 | "deadzone": 0.5, 86 | "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":0,"physical_keycode":4194305,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) 87 | ] 88 | } 89 | 90 | [internationalization] 91 | 92 | locale/translations=PackedStringArray("res://i18n/translation.de.translation", "res://i18n/translation.en.translation") 93 | 94 | [rendering] 95 | 96 | environment/defaults/default_clear_color=Color(0.211765, 0.239216, 0.321569, 1) 97 | -------------------------------------------------------------------------------- /godot/savegame/save_game.gd: -------------------------------------------------------------------------------- 1 | ## Script that manages saving games. 2 | class_name SaveGame extends Node 3 | 4 | const ENABLED = true 5 | const ENCRYPTION_KEY = "godotrules" 6 | const SAVE_GAME_TEMPLATE = "savegame.save" 7 | const SAVE_GROUP_NAME = "Persist" 8 | const NODE_DATA = "node_data" 9 | 10 | static func delete_save() -> void: 11 | 12 | if not ENABLED: 13 | return 14 | 15 | DirAccess.remove_absolute("user://" + SAVE_GAME_TEMPLATE) 16 | 17 | static func has_save() -> bool: 18 | return FileAccess.file_exists("user://" + SAVE_GAME_TEMPLATE) 19 | 20 | static func save_game(tree:SceneTree): 21 | 22 | if not ENABLED: 23 | return 24 | 25 | print("Saving game to user://" + SAVE_GAME_TEMPLATE) 26 | 27 | var save_file = null 28 | 29 | if OS.is_debug_build(): 30 | save_file = FileAccess.open("user://" + SAVE_GAME_TEMPLATE, FileAccess.WRITE) 31 | else: 32 | save_file = FileAccess.open_encrypted_with_pass("user://" + SAVE_GAME_TEMPLATE, FileAccess.WRITE, ENCRYPTION_KEY) 33 | 34 | var save_nodes = tree.get_nodes_in_group(SAVE_GROUP_NAME) 35 | 36 | for node in save_nodes: 37 | 38 | var save_data = {} 39 | 40 | # Check the node is an instanced scene so it can be instanced again during load. 41 | if not node.scene_file_path.is_empty(): 42 | save_data["scene_file_path"] = node.scene_file_path 43 | 44 | if not node.get_path().is_empty(): 45 | save_data["path"] = node.get_path() 46 | 47 | if not node.get_parent().get_path().is_empty(): 48 | save_data["parent"] = node.get_parent().get_path() 49 | 50 | if "position" in node: 51 | save_data["pos_x"] = node.position.x 52 | save_data["pos_y"] = node.position.y 53 | if node.position is Vector3: 54 | save_data["pos_z"] = node.position.z 55 | 56 | if node is Node2D: 57 | save_data["rotation"] = node.rotation 58 | elif node is Node3D: 59 | save_data["rotation_x"] = node.rotation.x 60 | save_data["rotation_y"] = node.rotation.y 61 | save_data["rotation_z"] = node.rotation.z 62 | 63 | if "scale" in node: 64 | save_data["scale_x"] = node.scale.x 65 | save_data["scale_y"] = node.scale.y 66 | if node.scale is Vector3: 67 | save_data["scale_z"] = node.scale.z 68 | 69 | save_data["visible"] = node.visible 70 | 71 | if node is CanvasItem: 72 | save_data["modulate_r"] = node.modulate.r 73 | save_data["modulate_g"] = node.modulate.g 74 | save_data["modulate_b"] = node.modulate.b 75 | save_data["modulate_a"] = node.modulate.a 76 | 77 | # Call the node's save function. 78 | if node.has_method("save_data"): 79 | save_data["node_data"] = node.call("save_data") 80 | 81 | # Store the save dictionary as a new line in the save file. 82 | save_file.store_line(JSON.stringify(save_data)) 83 | 84 | static func load_game(tree:SceneTree) -> void: 85 | 86 | if not ENABLED: 87 | return 88 | 89 | if not has_save(): 90 | print("No save game found. Skipped loading!") 91 | return 92 | 93 | print("Load game from user://" + SAVE_GAME_TEMPLATE) 94 | 95 | var save_nodes = tree.get_nodes_in_group(SAVE_GROUP_NAME) 96 | 97 | var nodes_by_path = {} 98 | for node in save_nodes: 99 | if not node.get_path().is_empty(): 100 | nodes_by_path[node.get_path()] = node 101 | 102 | # Load the file line by line and process that dictionary to restore 103 | # the object it represents. 104 | var save_file = null 105 | 106 | if OS.is_debug_build(): 107 | save_file = FileAccess.open("user://" + SAVE_GAME_TEMPLATE, FileAccess.READ) 108 | else: 109 | save_file = FileAccess.open_encrypted_with_pass("user://" + SAVE_GAME_TEMPLATE, FileAccess.READ, ENCRYPTION_KEY) 110 | 111 | while save_file.get_position() < save_file.get_length(): 112 | # Get the saved dictionary from the next line in the save file 113 | var test_json_conv = JSON.new() 114 | test_json_conv.parse(save_file.get_line()) 115 | var save_data = test_json_conv.get_data() 116 | 117 | # Firstly, we need to create the object and add it to the tree and set its position. 118 | var node = null 119 | 120 | if "path" in save_data and nodes_by_path.has(NodePath(save_data.path)): 121 | node = nodes_by_path[NodePath(save_data.path)] 122 | nodes_by_path.erase(NodePath(save_data.path)) 123 | elif "path" in save_data and "parent" in save_data and "scene_file_path" in save_data: 124 | # node is not present in tree so it was dynamically added at runtime 125 | var parent = tree.root.get_node(NodePath(save_data["parent"])) 126 | node = load(save_data["scene_file_path"]).instantiate() 127 | parent.add_child(node) 128 | else: 129 | push_warning("skipping loading node from save game: node got moved.") 130 | continue 131 | 132 | if "position" in node: 133 | if node.scale is Vector2: 134 | node.position = Vector2(save_data["pos_x"], save_data["pos_y"]) 135 | elif node.scale is Vector3: 136 | node.position = Vector3(save_data["pos_x"], save_data["pos_y"], save_data["pos_z"]) 137 | 138 | if node is Node2D: 139 | node.rotation = save_data["rotation"] 140 | elif node is Node3D: 141 | node.rotation = Vector3(save_data["rotation_x"], save_data["rotation_y"], save_data["rotation_z"]) 142 | 143 | if "scale" in node: 144 | if node.scale is Vector2: 145 | node.scale = Vector2(save_data["scale_x"], save_data["scale_y"]) 146 | elif node.scale is Vector3: 147 | node.scale = Vector3(save_data["scale_x"], save_data["scale_y"], save_data["scale_z"]) 148 | 149 | if save_data.has("visible") and "visible" in node: 150 | node.visible = save_data["visible"] 151 | 152 | if node is CanvasItem: 153 | node.modulate = Color(save_data["modulate_r"], save_data["modulate_g"], save_data["modulate_b"], save_data["modulate_a"]) 154 | 155 | if node.has_method("load_data") and save_data.has("node_data"): 156 | node.call("load_data", save_data["node_data"]) 157 | 158 | # delete any node from scene that was not persisted into the save file 159 | # but is currently tagged as "Persisted" -> this means node got removed in the meantime 160 | for key in nodes_by_path: 161 | var node = nodes_by_path[key] 162 | node.queue_free() 163 | -------------------------------------------------------------------------------- /godot/savegame/save_game.gd.uid: -------------------------------------------------------------------------------- 1 | uid://b634ar6kikhq6 2 | -------------------------------------------------------------------------------- /godot/scenes/boot/bootsplash_scene.gd: -------------------------------------------------------------------------------- 1 | class_name BootsplashScene 2 | extends Control 3 | 4 | @export var fade_duration:float = 1.0 5 | @export var stay_duration:float = 1.5 6 | @export var node:PackedScene 7 | @export var next_scene:PackedScene 8 | @export var interuptable:bool = true 9 | 10 | @onready var control = %NodeContainer 11 | @onready var instance:Node2D = node.instantiate() 12 | 13 | func _ready(): 14 | instance.modulate.a = 0.0 15 | control.add_child(instance) 16 | var tween = create_tween() 17 | tween.set_trans(Tween.TRANS_CUBIC) 18 | tween.set_ease(Tween.EASE_IN) 19 | tween.tween_property(instance, "modulate:a", 1.0, fade_duration)\ 20 | .from(0.0)\ 21 | .finished.connect(_fade_out) 22 | 23 | func _process(_delta): 24 | if interuptable and Input.is_action_just_pressed("exit"): 25 | _change_scene() 26 | 27 | func _fade_out(): 28 | var tween = create_tween() 29 | tween.set_trans(Tween.TRANS_CUBIC) 30 | tween.set_ease(Tween.EASE_IN) 31 | tween.tween_property(instance, "modulate:a", 0.0, fade_duration)\ 32 | .set_delay(stay_duration)\ 33 | .finished.connect(_change_scene) 34 | 35 | func _change_scene(): 36 | get_tree().change_scene_to_packed(next_scene) 37 | -------------------------------------------------------------------------------- /godot/scenes/boot/bootsplash_scene.gd.uid: -------------------------------------------------------------------------------- 1 | uid://psoe3k1dufrt 2 | -------------------------------------------------------------------------------- /godot/scenes/boot/bootsplash_scene.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://ehrvsjvp61gu"] 2 | 3 | [ext_resource type="Script" uid="uid://psoe3k1dufrt" path="res://scenes/boot/bootsplash_scene.gd" id="2"] 4 | 5 | [node name="BootsplashScene" type="Control"] 6 | layout_mode = 3 7 | anchors_preset = 15 8 | anchor_right = 1.0 9 | anchor_bottom = 1.0 10 | grow_horizontal = 2 11 | grow_vertical = 2 12 | script = ExtResource("2") 13 | fade_duration = 2.0 14 | 15 | [node name="ColorRect" type="ColorRect" parent="."] 16 | anchors_preset = 15 17 | anchor_right = 1.0 18 | anchor_bottom = 1.0 19 | grow_horizontal = 2 20 | grow_vertical = 2 21 | color = Color(0, 0, 0, 1) 22 | 23 | [node name="CenterContainer" type="CenterContainer" parent="."] 24 | anchors_preset = 15 25 | anchor_right = 1.0 26 | anchor_bottom = 1.0 27 | grow_horizontal = 2 28 | grow_vertical = 2 29 | 30 | [node name="NodeContainer" type="Control" parent="CenterContainer"] 31 | unique_name_in_owner = true 32 | layout_mode = 3 33 | anchors_preset = 0 34 | offset_left = 288.0 35 | offset_top = 162.0 36 | offset_right = 288.0 37 | offset_bottom = 162.0 38 | 39 | [node name="Camera2D" type="Camera2D" parent="."] 40 | anchor_mode = 0 41 | current = true 42 | -------------------------------------------------------------------------------- /godot/scenes/boot/godot/godot_bootsplash_scene.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=3 uid="uid://cx5xs0x7y0i3o"] 2 | 3 | [ext_resource type="Theme" uid="uid://d22mpqutkotsu" path="res://ui/theme.tres" id="1_il62e"] 4 | [ext_resource type="PackedScene" uid="uid://ehrvsjvp61gu" path="res://scenes/boot/bootsplash_scene.tscn" id="2_ixabf"] 5 | [ext_resource type="PackedScene" uid="uid://cmmr5jbylu08i" path="res://scenes/boot/godot/godot_logo.tscn" id="3_e0rf4"] 6 | [ext_resource type="PackedScene" uid="uid://dv2y18ye8j7o0" path="res://scenes/main_menu_scene.tscn" id="4_n5qhh"] 7 | 8 | [node name="GodotBootsplashScene" type="Control"] 9 | layout_mode = 3 10 | anchors_preset = 15 11 | anchor_right = 1.0 12 | anchor_bottom = 1.0 13 | grow_horizontal = 2 14 | grow_vertical = 2 15 | theme = ExtResource("1_il62e") 16 | 17 | [node name="BootsplashScene" parent="." instance=ExtResource("2_ixabf")] 18 | layout_mode = 1 19 | theme = ExtResource("1_il62e") 20 | node = ExtResource("3_e0rf4") 21 | next_scene = ExtResource("4_n5qhh") 22 | -------------------------------------------------------------------------------- /godot/scenes/boot/godot/godot_logo.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://cmmr5jbylu08i"] 2 | 3 | [ext_resource type="Texture2D" uid="uid://dbi3kwcagajfd" path="res://scenes/boot/logo.png" id="1_642wf"] 4 | [ext_resource type="FontFile" uid="uid://le2vdo2626vw" path="res://fonts/Montserrat-Medium.ttf" id="2_b778c"] 5 | 6 | [sub_resource type="LabelSettings" id="LabelSettings_cbnh2"] 7 | 8 | [node name="GodotLogo" type="Node2D"] 9 | 10 | [node name="Sprite2D" type="Sprite2D" parent="."] 11 | position = Vector2(0, -19) 12 | scale = Vector2(0.08, 0.08) 13 | texture = ExtResource("1_642wf") 14 | 15 | [node name="Label" type="Label" parent="."] 16 | offset_left = -105.0 17 | offset_top = 15.0 18 | offset_right = 105.0 19 | offset_bottom = 56.0 20 | theme_override_fonts/font = ExtResource("2_b778c") 21 | theme_override_font_sizes/font_size = 20 22 | text = "Godot Engine" 23 | label_settings = SubResource("LabelSettings_cbnh2") 24 | horizontal_alignment = 1 25 | vertical_alignment = 1 26 | -------------------------------------------------------------------------------- /godot/scenes/boot/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitbrain/godot-gamejam/53baefb2348c4ed8a0e7d0211b26ec3097c4cf56/godot/scenes/boot/logo.png -------------------------------------------------------------------------------- /godot/scenes/boot/logo.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://dbi3kwcagajfd" 6 | path="res://.godot/imported/logo.png-90d210f2abe4f4002f845fb0a1ede447.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://scenes/boot/logo.png" 14 | dest_files=["res://.godot/imported/logo.png-90d210f2abe4f4002f845fb0a1ede447.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=0 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=1 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=0 35 | -------------------------------------------------------------------------------- /godot/scenes/game_settings_scene.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | @onready var overlay := %FadeOverlay 4 | @onready var return_button := %ReturnButton 5 | 6 | func _ready(): 7 | overlay.on_complete_fade_out.connect(_on_fade_overlay_on_complete_fade_out) 8 | return_button.pressed.connect(_on_return_button_pressed) 9 | 10 | overlay.visible = true 11 | return_button.grab_focus() 12 | 13 | func _on_fade_overlay_on_complete_fade_out(): 14 | get_tree().change_scene_to_file("res://scenes/main_menu_scene.tscn") 15 | 16 | func _on_return_button_pressed(): 17 | overlay.fade_out() 18 | -------------------------------------------------------------------------------- /godot/scenes/game_settings_scene.gd.uid: -------------------------------------------------------------------------------- 1 | uid://ckdn2dpa7o7ra 2 | -------------------------------------------------------------------------------- /godot/scenes/game_settings_scene.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=3 uid="uid://g4qhvbvdm62w"] 2 | 3 | [ext_resource type="Script" uid="uid://ckdn2dpa7o7ra" path="res://scenes/game_settings_scene.gd" id="1_3764u"] 4 | [ext_resource type="PackedScene" uid="uid://bkk87o2ooo6at" path="res://ui/overlays/fade_overlay.tscn" id="1_w6tmj"] 5 | [ext_resource type="PackedScene" uid="uid://nbaodrlopo7y" path="res://ui/components/game_settings.tscn" id="2_qsqie"] 6 | [ext_resource type="Theme" uid="uid://d22mpqutkotsu" path="res://ui/theme.tres" id="3_unbh4"] 7 | 8 | [node name="GameSettingsScene" type="Node2D"] 9 | script = ExtResource("1_3764u") 10 | 11 | [node name="UI" type="CanvasLayer" parent="."] 12 | 13 | [node name="CenterContainer" type="CenterContainer" parent="UI"] 14 | anchors_preset = 15 15 | anchor_right = 1.0 16 | anchor_bottom = 1.0 17 | grow_horizontal = 2 18 | grow_vertical = 2 19 | 20 | [node name="VBoxContainer" type="VBoxContainer" parent="UI/CenterContainer"] 21 | layout_mode = 2 22 | theme_override_constants/separation = 20 23 | 24 | [node name="SettingsLabel" type="Label" parent="UI/CenterContainer/VBoxContainer"] 25 | layout_mode = 2 26 | theme_type_variation = &"HeaderLarge" 27 | text = "settings" 28 | 29 | [node name="GameSettings" parent="UI/CenterContainer/VBoxContainer" instance=ExtResource("2_qsqie")] 30 | layout_mode = 2 31 | 32 | [node name="ReturnButton" type="Button" parent="UI/CenterContainer/VBoxContainer"] 33 | unique_name_in_owner = true 34 | layout_mode = 2 35 | theme = ExtResource("3_unbh4") 36 | text = "return_to_main" 37 | 38 | [node name="FadeOverlay" parent="UI" instance=ExtResource("1_w6tmj")] 39 | unique_name_in_owner = true 40 | visible = false 41 | 42 | [node name="Music" type="AudioStreamPlayer" parent="."] 43 | autoplay = true 44 | bus = &"Music" 45 | -------------------------------------------------------------------------------- /godot/scenes/ingame_scene.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | @onready var fade_overlay = %FadeOverlay 4 | @onready var pause_overlay = %PauseOverlay 5 | 6 | func _ready() -> void: 7 | fade_overlay.visible = true 8 | 9 | if SaveGame.has_save(): 10 | SaveGame.load_game(get_tree()) 11 | 12 | pause_overlay.game_exited.connect(_save_game) 13 | 14 | func _input(event) -> void: 15 | if event.is_action_pressed("pause") and not pause_overlay.visible: 16 | get_viewport().set_input_as_handled() 17 | get_tree().paused = true 18 | pause_overlay.grab_button_focus() 19 | pause_overlay.visible = true 20 | 21 | func _save_game() -> void: 22 | SaveGame.save_game(get_tree()) 23 | -------------------------------------------------------------------------------- /godot/scenes/ingame_scene.gd.uid: -------------------------------------------------------------------------------- 1 | uid://ovjegpk30m4h 2 | -------------------------------------------------------------------------------- /godot/scenes/ingame_scene.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=3 uid="uid://cik30de5gaaah"] 2 | 3 | [ext_resource type="Script" uid="uid://ovjegpk30m4h" path="res://scenes/ingame_scene.gd" id="1_objyc"] 4 | [ext_resource type="PackedScene" uid="uid://bkk87o2ooo6at" path="res://ui/overlays/fade_overlay.tscn" id="1_y6ebv"] 5 | [ext_resource type="Texture2D" uid="uid://dmelj2qgr8w7k" path="res://icon.svg" id="2_h1yxu"] 6 | [ext_resource type="PackedScene" uid="uid://jyv4g54adkmo" path="res://ui/overlays/pause_overlay.tscn" id="3_8o178"] 7 | [ext_resource type="Script" uid="uid://cu3npowt4r1px" path="res://scenes/node_example.gd" id="3_a5686"] 8 | 9 | [node name="IngameScene" type="Node2D"] 10 | script = ExtResource("1_objyc") 11 | 12 | [node name="NodeExample" type="Sprite2D" parent="." groups=["Persist"]] 13 | texture = ExtResource("2_h1yxu") 14 | script = ExtResource("3_a5686") 15 | 16 | [node name="UI" type="CanvasLayer" parent="."] 17 | 18 | [node name="FadeOverlay" parent="UI" instance=ExtResource("1_y6ebv")] 19 | unique_name_in_owner = true 20 | visible = false 21 | 22 | [node name="PauseOverlay" parent="UI" instance=ExtResource("3_8o178")] 23 | unique_name_in_owner = true 24 | process_mode = 2 25 | visible = false 26 | -------------------------------------------------------------------------------- /godot/scenes/intro_scene.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://nl1cn61u3wd"] 2 | 3 | [ext_resource type="PackedScene" uid="uid://bo4ehnumovbi" path="res://ui/components/bootsplash.tscn" id="1_gflph"] 4 | 5 | [node name="IntroScene" type="Node2D"] 6 | 7 | [node name="CanvasLayer" type="CanvasLayer" parent="."] 8 | 9 | [node name="Bootsplash" parent="CanvasLayer" instance=ExtResource("1_gflph")] 10 | -------------------------------------------------------------------------------- /godot/scenes/main_menu_scene.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | @export var game_scene:PackedScene 4 | @export var settings_scene:PackedScene 5 | 6 | @onready var overlay := %FadeOverlay 7 | @onready var continue_button := %ContinueButton 8 | @onready var new_game_button := %NewGameButton 9 | @onready var settings_button := %SettingsButton 10 | @onready var exit_button := %ExitButton 11 | 12 | var next_scene = game_scene 13 | var new_game = true 14 | 15 | func _ready() -> void: 16 | overlay.visible = true 17 | new_game_button.disabled = game_scene == null 18 | settings_button.disabled = settings_scene == null 19 | continue_button.visible = SaveGame.has_save() and SaveGame.ENABLED 20 | 21 | # connect signals 22 | new_game_button.pressed.connect(_on_play_button_pressed) 23 | continue_button.pressed.connect(_on_continue_button_pressed) 24 | settings_button.pressed.connect(_on_settings_button_pressed) 25 | exit_button.pressed.connect(_on_exit_button_pressed) 26 | overlay.on_complete_fade_out.connect(_on_fade_overlay_on_complete_fade_out) 27 | 28 | if continue_button.visible: 29 | continue_button.grab_focus() 30 | else: 31 | new_game_button.grab_focus() 32 | 33 | func _on_settings_button_pressed() -> void: 34 | new_game = false 35 | next_scene = settings_scene 36 | overlay.fade_out() 37 | 38 | func _on_play_button_pressed() -> void: 39 | next_scene = game_scene 40 | overlay.fade_out() 41 | 42 | func _on_continue_button_pressed() -> void: 43 | new_game = false 44 | next_scene = game_scene 45 | overlay.fade_out() 46 | 47 | func _on_exit_button_pressed() -> void: 48 | get_tree().quit() 49 | 50 | func _on_fade_overlay_on_complete_fade_out() -> void: 51 | if new_game and SaveGame.has_save(): 52 | SaveGame.delete_save() 53 | get_tree().change_scene_to_packed(next_scene) 54 | -------------------------------------------------------------------------------- /godot/scenes/main_menu_scene.gd.uid: -------------------------------------------------------------------------------- 1 | uid://k5f271qgai2d 2 | -------------------------------------------------------------------------------- /godot/scenes/main_menu_scene.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=7 format=3 uid="uid://dv2y18ye8j7o0"] 2 | 3 | [ext_resource type="Script" uid="uid://k5f271qgai2d" path="res://scenes/main_menu_scene.gd" id="1_1foie"] 4 | [ext_resource type="PackedScene" uid="uid://cik30de5gaaah" path="res://scenes/ingame_scene.tscn" id="2_nl5i6"] 5 | [ext_resource type="PackedScene" uid="uid://g4qhvbvdm62w" path="res://scenes/game_settings_scene.tscn" id="3_dgs4d"] 6 | [ext_resource type="PackedScene" uid="uid://bkk87o2ooo6at" path="res://ui/overlays/fade_overlay.tscn" id="3_kf1us"] 7 | [ext_resource type="PackedScene" uid="uid://b4blrdjthcxnn" path="res://ui/components/game_logo.tscn" id="4_fnufg"] 8 | 9 | [sub_resource type="LabelSettings" id="LabelSettings_2ikbh"] 10 | font_size = 8 11 | font_color = Color(1, 1, 1, 0.486275) 12 | 13 | [node name="MainMenuScene" type="Node2D"] 14 | script = ExtResource("1_1foie") 15 | game_scene = ExtResource("2_nl5i6") 16 | settings_scene = ExtResource("3_dgs4d") 17 | 18 | [node name="UI" type="CanvasLayer" parent="."] 19 | 20 | [node name="CenterContainer" type="CenterContainer" parent="UI"] 21 | anchors_preset = 15 22 | anchor_right = 1.0 23 | anchor_bottom = 1.0 24 | grow_horizontal = 2 25 | grow_vertical = 2 26 | 27 | [node name="VBoxContainer" type="VBoxContainer" parent="UI/CenterContainer"] 28 | layout_mode = 2 29 | theme_override_constants/separation = 5 30 | 31 | [node name="GameLogo" parent="UI/CenterContainer/VBoxContainer" instance=ExtResource("4_fnufg")] 32 | layout_mode = 2 33 | 34 | [node name="ContinueButton" type="Button" parent="UI/CenterContainer/VBoxContainer"] 35 | unique_name_in_owner = true 36 | layout_mode = 2 37 | text = "continue" 38 | 39 | [node name="NewGameButton" type="Button" parent="UI/CenterContainer/VBoxContainer"] 40 | unique_name_in_owner = true 41 | custom_minimum_size = Vector2(180, 0) 42 | layout_mode = 2 43 | size_flags_horizontal = 4 44 | text = "new_game" 45 | 46 | [node name="SettingsButton" type="Button" parent="UI/CenterContainer/VBoxContainer"] 47 | unique_name_in_owner = true 48 | custom_minimum_size = Vector2(180, 0) 49 | layout_mode = 2 50 | size_flags_horizontal = 4 51 | text = "settings" 52 | 53 | [node name="ExitButton" type="Button" parent="UI/CenterContainer/VBoxContainer"] 54 | unique_name_in_owner = true 55 | custom_minimum_size = Vector2(180, 0) 56 | layout_mode = 2 57 | size_flags_horizontal = 4 58 | text = "leave_game" 59 | 60 | [node name="Credits" type="MarginContainer" parent="UI/CenterContainer/VBoxContainer"] 61 | layout_mode = 2 62 | size_flags_horizontal = 4 63 | theme_override_constants/margin_top = 10 64 | 65 | [node name="Label" type="Label" parent="UI/CenterContainer/VBoxContainer/Credits"] 66 | layout_mode = 2 67 | text = "credits" 68 | label_settings = SubResource("LabelSettings_2ikbh") 69 | horizontal_alignment = 1 70 | 71 | [node name="FadeOverlay" parent="UI" instance=ExtResource("3_kf1us")] 72 | unique_name_in_owner = true 73 | visible = false 74 | 75 | [node name="Music" type="AudioStreamPlayer" parent="."] 76 | autoplay = true 77 | bus = &"Music" 78 | -------------------------------------------------------------------------------- /godot/scenes/node_example.gd: -------------------------------------------------------------------------------- 1 | extends Sprite2D 2 | 3 | # specify in nodes to load data 4 | # from save game for this node 5 | func load_data(data:Dictionary) -> void: 6 | print(data.hello) 7 | 8 | # specify in nodes to save data 9 | # of this node to the save game 10 | func save_data() -> Dictionary: 11 | return { 12 | "hello": "Hello Godot!" 13 | } 14 | 15 | func _process(delta): 16 | if Input.is_action_pressed("ui_right"): 17 | position.x += 100 * delta 18 | if Input.is_action_pressed("ui_left"): 19 | position.x -= 100 * delta 20 | if Input.is_action_pressed("ui_up"): 21 | position.y -= 100 * delta 22 | if Input.is_action_pressed("ui_down"): 23 | position.y += 100 * delta 24 | -------------------------------------------------------------------------------- /godot/scenes/node_example.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cu3npowt4r1px 2 | -------------------------------------------------------------------------------- /godot/settings/user_settings.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | signal on_value_change(key, value) 4 | 5 | const SECTION = "user" 6 | const SETTINGS_FILE = "user://settings.cfg" 7 | 8 | const MASTERVOLUME_ENABLED = "mastervolume_enabled" 9 | const MUSICVOLUME_ENABLED = "musicvolume_enabled" 10 | const SOUNDVOLUME_ENABLED = "soundvolume_enabled" 11 | const MASTERVOLUME = "mastervolume" 12 | const MUSICVOLUME = "musicvolume" 13 | const SOUNDVOLUME = "soundvolume" 14 | const GAME_LANGUAGE = "game_locale" 15 | 16 | const AUDIO_BUS_MASTER = "Master" 17 | const AUDIO_BUS_SOUND = "Sound" 18 | const AUDIO_BUS_MUSIC = "Music" 19 | 20 | var USER_SETTING_DEFAULTS = { 21 | MASTERVOLUME_ENABLED:true, 22 | MUSICVOLUME_ENABLED:true, 23 | SOUNDVOLUME_ENABLED:true, 24 | MASTERVOLUME:100, 25 | MUSICVOLUME:70, 26 | SOUNDVOLUME:100, 27 | GAME_LANGUAGE:"en" 28 | } 29 | 30 | var config:ConfigFile 31 | 32 | func _ready(): 33 | config = ConfigFile.new() 34 | config.load(SETTINGS_FILE) 35 | _configure_audio() 36 | _configure_language() 37 | 38 | func set_value(key, value): 39 | config.set_value(SECTION, key, value) 40 | config.save(SETTINGS_FILE) 41 | if key == MASTERVOLUME: 42 | _update_volume(MASTERVOLUME, AUDIO_BUS_MASTER) 43 | if key == SOUNDVOLUME: 44 | _update_volume(SOUNDVOLUME, AUDIO_BUS_SOUND) 45 | if key == MUSICVOLUME: 46 | _update_volume(MUSICVOLUME, AUDIO_BUS_MUSIC) 47 | if key == MASTERVOLUME_ENABLED: 48 | _mute_bus(MASTERVOLUME_ENABLED, AUDIO_BUS_MASTER) 49 | if key == MUSICVOLUME_ENABLED: 50 | _mute_bus(MUSICVOLUME_ENABLED, AUDIO_BUS_MUSIC) 51 | if key == SOUNDVOLUME_ENABLED: 52 | _mute_bus(SOUNDVOLUME_ENABLED, AUDIO_BUS_SOUND) 53 | if key == GAME_LANGUAGE: 54 | TranslationServer.set_locale(value) 55 | emit_signal("on_value_change", key, value) 56 | 57 | func get_value(key): 58 | return config.get_value(SECTION, key, _get_default(key)) 59 | 60 | func get_value_with_default(key, default): 61 | return config.get_value(SECTION, key, default) 62 | 63 | func _get_default(key): 64 | if USER_SETTING_DEFAULTS.has(key): 65 | return USER_SETTING_DEFAULTS[key] 66 | return null 67 | 68 | func _configure_audio(): 69 | _update_volume(MASTERVOLUME, AUDIO_BUS_MASTER) 70 | _update_volume(MUSICVOLUME, AUDIO_BUS_MUSIC) 71 | _update_volume(SOUNDVOLUME, AUDIO_BUS_SOUND) 72 | _mute_bus(MASTERVOLUME_ENABLED, AUDIO_BUS_MASTER) 73 | _mute_bus(MUSICVOLUME_ENABLED, AUDIO_BUS_MUSIC) 74 | _mute_bus(SOUNDVOLUME_ENABLED, AUDIO_BUS_SOUND) 75 | 76 | func _update_volume(property, bus): 77 | var current = (get_value_with_default(property, USER_SETTING_DEFAULTS[property]) -100) / 2 78 | AudioServer.set_bus_volume_db(AudioServer.get_bus_index(bus), current) 79 | 80 | func _mute_bus(property, bus): 81 | var enabled = get_value_with_default(property, USER_SETTING_DEFAULTS[property]) 82 | AudioServer.set_bus_mute(AudioServer.get_bus_index(bus), not enabled) 83 | 84 | func _configure_language(): 85 | TranslationServer.set_locale(get_value_with_default(GAME_LANGUAGE, USER_SETTING_DEFAULTS[GAME_LANGUAGE])) 86 | -------------------------------------------------------------------------------- /godot/settings/user_settings.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cneysspdwsivq 2 | -------------------------------------------------------------------------------- /godot/ui/components/bootsplash.gd: -------------------------------------------------------------------------------- 1 | class_name Bootsplash 2 | extends Control 3 | -------------------------------------------------------------------------------- /godot/ui/components/bootsplash.gd.uid: -------------------------------------------------------------------------------- 1 | uid://62yttq4lfsqf 2 | -------------------------------------------------------------------------------- /godot/ui/components/bootsplash.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://bo4ehnumovbi"] 2 | 3 | [ext_resource type="Script" uid="uid://62yttq4lfsqf" path="res://ui/components/bootsplash.gd" id="1_3allk"] 4 | 5 | [node name="Bootsplash" type="Control"] 6 | layout_mode = 3 7 | anchors_preset = 0 8 | script = ExtResource("1_3allk") 9 | -------------------------------------------------------------------------------- /godot/ui/components/float_range_game_settings_option.gd: -------------------------------------------------------------------------------- 1 | extends HSlider 2 | 3 | @export var property:String = "" 4 | 5 | var initialised = false 6 | 7 | func _ready(): 8 | value = UserSettings.get_value(property) 9 | 10 | func _on_float_range_game_settings_option_value_changed(val): 11 | if !initialised: 12 | initialised = true 13 | UserSettings.set_value(property, val) 14 | -------------------------------------------------------------------------------- /godot/ui/components/float_range_game_settings_option.gd.uid: -------------------------------------------------------------------------------- 1 | uid://ds3psen8pvku 2 | -------------------------------------------------------------------------------- /godot/ui/components/float_range_game_settings_option.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://cpo1u33eut0i4"] 2 | 3 | [ext_resource type="Script" uid="uid://ds3psen8pvku" path="res://ui/components/float_range_game_settings_option.gd" id="1_hqqh3"] 4 | 5 | [node name="FloatRangeGameSettingsOption" type="HSlider"] 6 | custom_minimum_size = Vector2(100, 0) 7 | script = ExtResource("1_hqqh3") 8 | 9 | [connection signal="value_changed" from="." to="." method="_on_float_range_game_settings_option_value_changed"] 10 | -------------------------------------------------------------------------------- /godot/ui/components/game_logo.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://b4blrdjthcxnn"] 2 | 3 | [ext_resource type="Texture2D" uid="uid://dmelj2qgr8w7k" path="res://icon.svg" id="1_scjn5"] 4 | 5 | [node name="GameLogo" type="CenterContainer"] 6 | offset_right = 128.0 7 | offset_bottom = 128.0 8 | 9 | [node name="MarginContainer" type="MarginContainer" parent="."] 10 | offset_right = 148.0 11 | offset_bottom = 148.0 12 | theme_override_constants/margin_left = 10 13 | theme_override_constants/margin_top = 10 14 | theme_override_constants/margin_right = 10 15 | theme_override_constants/margin_bottom = 10 16 | 17 | [node name="TextureRect" type="TextureRect" parent="MarginContainer"] 18 | offset_left = 10.0 19 | offset_top = 10.0 20 | offset_right = 138.0 21 | offset_bottom = 138.0 22 | texture = ExtResource("1_scjn5") 23 | stretch_mode = 2 24 | -------------------------------------------------------------------------------- /godot/ui/components/game_settings.gd: -------------------------------------------------------------------------------- 1 | extends VBoxContainer 2 | 3 | @onready var master_volume_toggle := %MasterEnabledToggle 4 | @onready var master_volume_slider := %MasterVolumeSlider 5 | @onready var music_volume_toggle := %MusicEnabledToggle 6 | @onready var music_volume_slider := %MusicVolumeSlider 7 | @onready var sound_volume_toggle := %SoundEnabledToggle 8 | @onready var sound_volume_slider := %SoundVolumeSlider 9 | @onready var language_dropdown := %LanguageDropdown 10 | 11 | ## maps the index of a locale to the locale itself 12 | var locales:PackedStringArray = [] 13 | 14 | func _ready() -> void: 15 | self.locales = TranslationServer.get_loaded_locales() 16 | var current_locale = TranslationServer.get_locale() 17 | var idx = 0 18 | var select_index = -1 19 | for locale in locales: 20 | var language = TranslationServer.get_locale_name(locale) 21 | language_dropdown.add_item(language, idx) 22 | if current_locale == locale: 23 | select_index = idx 24 | idx += 1 25 | language_dropdown.select(select_index) 26 | 27 | 28 | func _on_master_volume_toggle_toggled(button_pressed: bool) -> void: 29 | master_volume_slider.editable = button_pressed 30 | music_volume_slider.editable = music_volume_toggle.button_pressed and button_pressed 31 | sound_volume_slider.editable = sound_volume_toggle.button_pressed and button_pressed 32 | UserSettings.set_value("mastervolume_enabled", button_pressed) 33 | 34 | 35 | func _on_music_enabled_toggle_toggled(button_pressed: bool) -> void: 36 | music_volume_slider.editable = master_volume_toggle.button_pressed and button_pressed 37 | UserSettings.set_value("musicvolume_enabled", button_pressed) 38 | 39 | 40 | func _on_sound_enabled_toggle_toggled(button_pressed: bool) -> void: 41 | sound_volume_slider.editable = master_volume_toggle.button_pressed and button_pressed 42 | UserSettings.set_value("soundvolume_enabled", button_pressed) 43 | 44 | 45 | func _on_language_dropdown_item_selected(index: int) -> void: 46 | UserSettings.set_value("game_locale", locales[index]) 47 | -------------------------------------------------------------------------------- /godot/ui/components/game_settings.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bo5tf3pqs11s6 2 | -------------------------------------------------------------------------------- /godot/ui/components/game_settings.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://nbaodrlopo7y"] 2 | 3 | [ext_resource type="PackedScene" uid="uid://cpo1u33eut0i4" path="res://ui/components/float_range_game_settings_option.tscn" id="1_0gpwr"] 4 | [ext_resource type="Script" uid="uid://bo5tf3pqs11s6" path="res://ui/components/game_settings.gd" id="1_eje8l"] 5 | [ext_resource type="Theme" uid="uid://d22mpqutkotsu" path="res://ui/theme.tres" id="2_mudwu"] 6 | 7 | [node name="GameSettings" type="VBoxContainer"] 8 | script = ExtResource("1_eje8l") 9 | 10 | [node name="MarginContainer" type="MarginContainer" parent="."] 11 | offset_right = 648.0 12 | offset_bottom = 138.0 13 | theme_override_constants/margin_top = 10 14 | 15 | [node name="GridContainer" type="GridContainer" parent="MarginContainer"] 16 | custom_minimum_size = Vector2(300, 0) 17 | offset_top = 10.0 18 | offset_right = 648.0 19 | offset_bottom = 138.0 20 | theme_override_constants/h_separation = 20 21 | theme_override_constants/v_separation = 8 22 | columns = 4 23 | 24 | [node name="Empty1" type="Label" parent="MarginContainer/GridContainer"] 25 | offset_top = 1.0 26 | offset_right = 257.0 27 | offset_bottom = 24.0 28 | 29 | [node name="Empty2" type="Label" parent="MarginContainer/GridContainer"] 30 | offset_left = 277.0 31 | offset_top = 1.0 32 | offset_right = 321.0 33 | offset_bottom = 24.0 34 | 35 | [node name="Language" type="Label" parent="MarginContainer/GridContainer"] 36 | offset_left = 341.0 37 | offset_right = 528.0 38 | offset_bottom = 26.0 39 | text = "settings_language" 40 | 41 | [node name="LanguageDropdown" type="OptionButton" parent="MarginContainer/GridContainer"] 42 | unique_name_in_owner = true 43 | offset_left = 548.0 44 | offset_right = 648.0 45 | offset_bottom = 26.0 46 | 47 | [node name="MasterVolumeEnabled" type="Label" parent="MarginContainer/GridContainer"] 48 | offset_top = 34.0 49 | offset_right = 257.0 50 | offset_bottom = 60.0 51 | theme = ExtResource("2_mudwu") 52 | text = "settings_volume_master_enabled" 53 | 54 | [node name="MasterEnabledToggle" type="CheckButton" parent="MarginContainer/GridContainer"] 55 | unique_name_in_owner = true 56 | offset_left = 277.0 57 | offset_top = 34.0 58 | offset_right = 321.0 59 | offset_bottom = 60.0 60 | button_pressed = true 61 | 62 | [node name="MasterVolumeLabel" type="Label" parent="MarginContainer/GridContainer"] 63 | offset_left = 341.0 64 | offset_top = 34.0 65 | offset_right = 528.0 66 | offset_bottom = 60.0 67 | text = "settings_volume_master" 68 | 69 | [node name="MasterVolumeSlider" parent="MarginContainer/GridContainer" instance=ExtResource("1_0gpwr")] 70 | unique_name_in_owner = true 71 | offset_left = 548.0 72 | offset_top = 34.0 73 | offset_right = 648.0 74 | offset_bottom = 50.0 75 | property = "mastervolume" 76 | 77 | [node name="MusicEnabledLabel" type="Label" parent="MarginContainer/GridContainer"] 78 | offset_top = 68.0 79 | offset_right = 257.0 80 | offset_bottom = 94.0 81 | text = "settings_volume_music_enabled" 82 | 83 | [node name="MusicEnabledToggle" type="CheckButton" parent="MarginContainer/GridContainer"] 84 | unique_name_in_owner = true 85 | offset_left = 277.0 86 | offset_top = 68.0 87 | offset_right = 321.0 88 | offset_bottom = 94.0 89 | button_pressed = true 90 | 91 | [node name="MusicVolumeLabel" type="Label" parent="MarginContainer/GridContainer"] 92 | offset_left = 341.0 93 | offset_top = 68.0 94 | offset_right = 528.0 95 | offset_bottom = 94.0 96 | text = "settings_volume_music" 97 | 98 | [node name="MusicVolumeSlider" parent="MarginContainer/GridContainer" instance=ExtResource("1_0gpwr")] 99 | unique_name_in_owner = true 100 | offset_left = 548.0 101 | offset_top = 68.0 102 | offset_right = 648.0 103 | offset_bottom = 84.0 104 | property = "musicvolume" 105 | 106 | [node name="SoundEnabledLabel" type="Label" parent="MarginContainer/GridContainer"] 107 | offset_top = 102.0 108 | offset_right = 257.0 109 | offset_bottom = 128.0 110 | text = "settings_volume_sound_enabled" 111 | 112 | [node name="SoundEnabledToggle" type="CheckButton" parent="MarginContainer/GridContainer"] 113 | unique_name_in_owner = true 114 | offset_left = 277.0 115 | offset_top = 102.0 116 | offset_right = 321.0 117 | offset_bottom = 128.0 118 | button_pressed = true 119 | 120 | [node name="SoundVolumeLabel" type="Label" parent="MarginContainer/GridContainer"] 121 | offset_left = 341.0 122 | offset_top = 102.0 123 | offset_right = 528.0 124 | offset_bottom = 128.0 125 | text = "settings_volume_sound" 126 | 127 | [node name="SoundVolumeSlider" parent="MarginContainer/GridContainer" instance=ExtResource("1_0gpwr")] 128 | unique_name_in_owner = true 129 | offset_left = 548.0 130 | offset_top = 102.0 131 | offset_right = 648.0 132 | offset_bottom = 118.0 133 | property = "soundvolume" 134 | 135 | [connection signal="item_selected" from="MarginContainer/GridContainer/LanguageDropdown" to="." method="_on_language_dropdown_item_selected"] 136 | [connection signal="toggled" from="MarginContainer/GridContainer/MasterEnabledToggle" to="." method="_on_master_volume_toggle_toggled"] 137 | [connection signal="toggled" from="MarginContainer/GridContainer/MusicEnabledToggle" to="." method="_on_music_enabled_toggle_toggled"] 138 | [connection signal="toggled" from="MarginContainer/GridContainer/SoundEnabledToggle" to="." method="_on_sound_enabled_toggle_toggled"] 139 | -------------------------------------------------------------------------------- /godot/ui/overlays/fade_overlay.gd: -------------------------------------------------------------------------------- 1 | class_name FadeOverlay 2 | extends ColorRect 3 | 4 | signal on_complete_fade_in 5 | signal on_complete_fade_out 6 | 7 | @export var fade_in_duration: float = 2.0 8 | @export var fade_out_duration: float = 1.0 9 | @export var auto_fade_in: bool = true 10 | @export var minimum_opacity: float = 1.0 11 | 12 | func _ready(): 13 | modulate.a = minimum_opacity 14 | if auto_fade_in: 15 | fade_in() 16 | 17 | func fade_in(): 18 | var tween = create_tween().set_trans(Tween.TRANS_CUBIC) 19 | tween.tween_property(self, "modulate", Color(modulate, 0.0), fade_in_duration)\ 20 | .finished.connect(_on_complete_fade_in) 21 | 22 | func fade_out(): 23 | var tween = create_tween().set_trans(Tween.TRANS_CUBIC) 24 | tween.tween_property(self, "modulate", Color(modulate, minimum_opacity), fade_out_duration)\ 25 | .finished.connect(_on_complete_fade_out) 26 | 27 | func _on_complete_fade_out(): 28 | emit_signal("on_complete_fade_out") 29 | 30 | func _on_complete_fade_in(): 31 | emit_signal("on_complete_fade_in") 32 | -------------------------------------------------------------------------------- /godot/ui/overlays/fade_overlay.gd.uid: -------------------------------------------------------------------------------- 1 | uid://dbbi2tqjwmyji 2 | -------------------------------------------------------------------------------- /godot/ui/overlays/fade_overlay.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://bkk87o2ooo6at"] 2 | 3 | [ext_resource type="Script" uid="uid://dbbi2tqjwmyji" path="res://ui/overlays/fade_overlay.gd" id="1_0elae"] 4 | 5 | [node name="FadeOverlay" type="ColorRect"] 6 | anchors_preset = 15 7 | anchor_right = 1.0 8 | anchor_bottom = 1.0 9 | grow_horizontal = 2 10 | grow_vertical = 2 11 | mouse_filter = 2 12 | color = Color(0, 0, 0, 1) 13 | script = ExtResource("1_0elae") 14 | fade_in_duration = 1.0 15 | -------------------------------------------------------------------------------- /godot/ui/overlays/pause_overlay.gd: -------------------------------------------------------------------------------- 1 | extends CenterContainer 2 | 3 | signal game_exited 4 | 5 | @onready var resume_button := %ResumeButton 6 | @onready var settings_button := %SettingsButton 7 | @onready var exit_button := %ExitButton 8 | @onready var settings_container := %SettingsContainer 9 | @onready var menu_container := %MenuContainer 10 | @onready var back_button := %BackButton 11 | 12 | func _ready() -> void: 13 | resume_button.pressed.connect(_resume) 14 | settings_button.pressed.connect(_settings) 15 | exit_button.pressed.connect(_exit) 16 | back_button.pressed.connect(_pause_menu) 17 | 18 | func grab_button_focus() -> void: 19 | resume_button.grab_focus() 20 | 21 | func _resume() -> void: 22 | get_tree().paused = false 23 | visible = false 24 | 25 | 26 | func _settings() -> void: 27 | menu_container.visible = false 28 | settings_container.visible = true 29 | back_button.grab_focus() 30 | 31 | func _exit() -> void: 32 | game_exited.emit() 33 | get_tree().quit() 34 | 35 | func _pause_menu() -> void: 36 | settings_container.visible = false 37 | menu_container.visible = true 38 | settings_button.grab_focus() 39 | 40 | func _unhandled_input(event): 41 | if event.is_action_pressed("pause") and visible: 42 | get_viewport().set_input_as_handled() 43 | if menu_container.visible: 44 | _resume() 45 | if settings_container.visible: 46 | _pause_menu() 47 | -------------------------------------------------------------------------------- /godot/ui/overlays/pause_overlay.gd.uid: -------------------------------------------------------------------------------- 1 | uid://xiphbph8v88o 2 | -------------------------------------------------------------------------------- /godot/ui/overlays/pause_overlay.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://jyv4g54adkmo"] 2 | 3 | [ext_resource type="Script" uid="uid://xiphbph8v88o" path="res://ui/overlays/pause_overlay.gd" id="1_q2yda"] 4 | [ext_resource type="PackedScene" uid="uid://nbaodrlopo7y" path="res://ui/components/game_settings.tscn" id="2_n78uc"] 5 | 6 | [node name="PauseOverlay" type="CenterContainer"] 7 | anchors_preset = 15 8 | anchor_right = 1.0 9 | anchor_bottom = 1.0 10 | grow_horizontal = 2 11 | grow_vertical = 2 12 | script = ExtResource("1_q2yda") 13 | 14 | [node name="VBoxContainer3" type="VBoxContainer" parent="."] 15 | layout_mode = 2 16 | 17 | [node name="Label" type="Label" parent="VBoxContainer3"] 18 | layout_mode = 2 19 | theme_type_variation = &"HeaderLarge" 20 | text = "game_paused" 21 | horizontal_alignment = 1 22 | 23 | [node name="SettingsContainer" type="VBoxContainer" parent="VBoxContainer3"] 24 | unique_name_in_owner = true 25 | visible = false 26 | layout_mode = 2 27 | 28 | [node name="GameSettings" parent="VBoxContainer3/SettingsContainer" instance=ExtResource("2_n78uc")] 29 | layout_mode = 2 30 | 31 | [node name="BackButton" type="Button" parent="VBoxContainer3/SettingsContainer"] 32 | unique_name_in_owner = true 33 | layout_mode = 2 34 | text = "return_to_menu" 35 | 36 | [node name="MenuContainer" type="VBoxContainer" parent="VBoxContainer3"] 37 | unique_name_in_owner = true 38 | layout_mode = 2 39 | theme_override_constants/separation = 8 40 | 41 | [node name="ResumeButton" type="Button" parent="VBoxContainer3/MenuContainer"] 42 | unique_name_in_owner = true 43 | layout_mode = 2 44 | text = "resume_game" 45 | 46 | [node name="SettingsButton" type="Button" parent="VBoxContainer3/MenuContainer"] 47 | unique_name_in_owner = true 48 | layout_mode = 2 49 | text = "settings" 50 | 51 | [node name="ExitButton" type="Button" parent="VBoxContainer3/MenuContainer"] 52 | unique_name_in_owner = true 53 | layout_mode = 2 54 | text = "leave_game" 55 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitbrain/godot-gamejam/53baefb2348c4ed8a0e7d0211b26ec3097c4cf56/logo.png --------------------------------------------------------------------------------