diff --git a/.forgejo/workflows/godot-ci.yml b/.forgejo/workflows/godot-ci.yml
new file mode 100644
index 0000000..cbb7300
--- /dev/null
+++ b/.forgejo/workflows/godot-ci.yml
@@ -0,0 +1,104 @@
+name: "godot-ci export"
+on: push
+
+# NOTE: If your `project.godot` is at the repository root, set `PROJECT_PATH` below to ".".
+
+env:
+ GODOT_VERSION: 4.3
+ EXPORT_NAME: test-project
+ PROJECT_PATH: test-project
+
+jobs:
+ export-windows:
+ name: Windows Export
+ runs-on: ubuntu-20.04
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ lfs: true
+ - name: Windows Build
+ uses: https://git.antonio-da.dev/antoniodell/godot-ci@main
+ run: |
+ mkdir -v -p ~/.local/share/godot/export_templates/
+ mkdir -v -p ~/.config/
+ mv /root/.config/godot ~/.config/godot
+ mv /root/.local/share/godot/export_templates/${GODOT_VERSION}.stable ~/.local/share/godot/export_templates/${GODOT_VERSION}.stable
+ 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: Upload Artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: windows
+ path: build/windows
+
+ export-linux:
+ name: Linux Export
+ runs-on: ubuntu-20.04
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ lfs: true
+ - name: Linux Build
+ uses: https://git.antonio-da.dev/antoniodell/godot-ci@main
+ run: |
+ mkdir -v -p ~/.local/share/godot/export_templates/
+ mv /root/.local/share/godot/export_templates/${GODOT_VERSION}.stable ~/.local/share/godot/export_templates/${GODOT_VERSION}.stable
+ mkdir -v -p build/linux
+ EXPORT_DIR="$(readlink -f build)"
+ cd $PROJECT_PATH
+ godot --headless --verbose --export-release "Linux/X11" "$EXPORT_DIR/linux/$EXPORT_NAME.x86_64"
+ - name: Upload Artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: linux
+ path: build/linux
+
+ export-web:
+ name: Web Export
+ runs-on: ubuntu-20.04
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ lfs: true
+ - name: Web Build
+ uses: https://git.antonio-da.dev/antoniodell/godot-ci@main
+ run: |
+ mkdir -v -p ~/.local/share/godot/export_templates/
+ mv /root/.local/share/godot/export_templates/${GODOT_VERSION}.stable ~/.local/share/godot/export_templates/${GODOT_VERSION}.stable
+ 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: Upload Artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: web
+ path: build/web
+
+ export-mac:
+ name: Mac Export
+ runs-on: ubuntu-20.04
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ lfs: true
+ - name: Mac Build
+ uses: https://git.antonio-da.dev/antoniodell/godot-ci@main
+ run: |
+ mkdir -v -p ~/.local/share/godot/export_templates/
+ mv /root/.local/share/godot/export_templates/${GODOT_VERSION}.stable ~/.local/share/godot/export_templates/${GODOT_VERSION}.stable
+ mkdir -v -p build/mac
+ EXPORT_DIR="$(readlink -f build)"
+ cd $PROJECT_PATH
+ godot --headless --verbose --export-release "macOS" "$EXPORT_DIR/mac/$EXPORT_NAME.zip"
+ - name: Upload Artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: mac
+ path: build/mac
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..abda75d
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,53 @@
+# Based on https://github.com/abarichello/godot-ci/blob/master/Dockerfile
+FROM ubuntu:jammy
+
+USER root
+SHELL ["/bin/bash", "-c"]
+ENV DEBIAN_FRONTEND=noninteractive
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ ca-certificates \
+ git \
+ git-lfs \
+ unzip \
+ wget \
+ zip \
+ adb \
+ openjdk-17-jdk-headless \
+ rsync \
+ wine64 \
+ osslsigncode \
+ && rm -rf /var/lib/apt/lists/*
+
+# When in doubt, see the downloads page: https://godotengine.org/download/archive/
+ARG GODOT_VERSION="4.3"
+
+# Example values: stable, beta3, rc1, dev2, etc.
+# Also change the `SUBDIR` argument below when NOT using stable.
+ARG RELEASE_NAME="stable"
+
+ARG GODOT_TEST_ARGS=""
+ARG GODOT_PLATFORM="linux.x86_64"
+
+RUN wget https://github.com/godotengine/godot-builds/releases/download/${GODOT_VERSION}-${RELEASE_NAME}/Godot_v${GODOT_VERSION}-${RELEASE_NAME}_${GODOT_PLATFORM}.zip \
+ && wget https://github.com/godotengine/godot-builds/releases/download/${GODOT_VERSION}-${RELEASE_NAME}/Godot_v${GODOT_VERSION}-${RELEASE_NAME}_export_templates.tpz \
+ && mkdir ~/.cache \
+ && mkdir -p ~/.config/godot \
+ && mkdir -p ~/.local/share/godot/export_templates/${GODOT_VERSION}.${RELEASE_NAME} \
+ && unzip Godot_v${GODOT_VERSION}-${RELEASE_NAME}_${GODOT_PLATFORM}.zip \
+ && mv Godot_v${GODOT_VERSION}-${RELEASE_NAME}_${GODOT_PLATFORM} /usr/local/bin/godot \
+ && unzip Godot_v${GODOT_VERSION}-${RELEASE_NAME}_export_templates.tpz \
+ && mv templates/* ~/.local/share/godot/export_templates/${GODOT_VERSION}.${RELEASE_NAME} \
+ && rm -f Godot_v${GODOT_VERSION}-${RELEASE_NAME}_export_templates.tpz Godot_v${GODOT_VERSION}-${RELEASE_NAME}_${GODOT_PLATFORM}.zip
+
+
+RUN godot -v -e --quit --headless ${GODOT_TEST_ARGS}
+# Godot editor settings are stored per minor version since 4.3.
+# `${GODOT_VERSION:0:3}` transforms a string of the form `x.y.z` into `x.y`, even if it's already `x.y` (until Godot 4.9).
+RUN echo '[gd_resource type="EditorSettings" format=3]' > ~/.config/godot/editor_settings-${GODOT_VERSION:0:3}.tres
+RUN echo '[resource]' >> ~/.config/godot/editor_settings-${GODOT_VERSION:0:3}.tres
+
+
+# Download and set up rcedit to change Windows executable icons on export.
+RUN wget https://github.com/electron/rcedit/releases/download/v2.0.0/rcedit-x64.exe -O /opt/rcedit.exe
+RUN echo 'export/windows/rcedit = "/opt/rcedit.exe"' >> ~/.config/godot/editor_settings-${GODOT_VERSION:0:3}.tres
+RUN echo 'export/windows/wine = "/usr/bin/wine64-stable"' >> ~/.config/godot/editor_settings-${GODOT_VERSION:0:3}.tres
\ No newline at end of file
diff --git a/test-project/.gitignore b/test-project/.gitignore
new file mode 100755
index 0000000..da309cf
--- /dev/null
+++ b/test-project/.gitignore
@@ -0,0 +1,312 @@
+# Godot auto generated files
+*.gen.*
+**/.import/
+
+# Documentation generated by doxygen or from classes.xml
+doc/_build/
+
+# Javascript specific
+*.bc
+
+# Android specific
+platform/android/java/build.gradle
+platform/android/java/.gradle
+platform/android/java/.gradletasknamecache
+platform/android/java/local.properties
+platform/android/java/project.properties
+platform/android/java/build.gradle
+platform/android/java/AndroidManifest.xml
+platform/android/java/libs/*
+platform/android/java/assets
+
+# General c++ generated files
+*.lib
+*.o
+*.ox
+*.a
+*.ax
+*.d
+*.so
+# Include gdsqlite lib
+!*.64.so
+!*.32.so
+*.os
+*.Plo
+*.lo
+
+# Libs generated files
+.deps/*
+.dirstamp
+
+# Gprof output
+gmon.out
+
+# Vim temp files
+*.swo
+*.swp
+
+# QT project files
+*.config
+*.creator
+*.files
+*.includes
+
+# Eclipse CDT files
+.cproject
+.settings/
+
+# Misc
+.DS_Store
+logs/
+
+# for projects that use SCons for building: http://http://www.scons.org/
+.sconf_temp
+.sconsign.dblite
+*.pyc
+
+
+# https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.sln.docstates
+*.sln
+*.vcxproj*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+x64/
+build/
+bld/
+[Bb]in/
+[Oo]bj/
+*.debug
+*.dSYM
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# Hints for improving IntelliSense, created together with VS project
+cpp.hint
+
+# Visualizers for the VS debugger
+*.natvis
+
+#NUNIT
+*.VisualState.xml
+TestResult.xml
+
+*.o
+*.a
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.opendb
+*.VC.VC.opendb
+enc_temp_folder/
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+
+# CodeLite project files
+*.project
+*.workspace
+.codelite/
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding addin-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+*.ncrunch*
+_NCrunch_*
+.*crunch*.local.xml
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+
+# NuGet Packages Directory
+## TODO: If you have NuGet Package Restore enabled, uncomment the next line
+#packages/*
+## TODO: If the tool you use requires repositories.config, also uncomment the next line
+#!packages/repositories.config
+
+# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
+# This line needs to be after the ignore of the build folder (and the packages folder if the line above has been uncommented)
+!packages/build/
+
+# Windows Azure Build Output
+csx/
+*.build.csdef
+
+# Windows Store app package directory
+AppPackages/
+
+# Others
+*.Cache
+ClientBin/
+[Ss]tyle[Cc]op.*
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.pfx
+*.publishsettings
+node_modules/
+
+# KDE
+.directory
+
+#Kdevelop project files
+*.kdev4
+
+# xCode
+xcuserdata
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file to a newer
+# Visual Studio version. Backup files are not needed, because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+App_Data/*.mdf
+App_Data/*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# =========================
+# Windows detritus
+# =========================
+
+# Windows image file caches
+Thumbs.db
+ehthumbs.db
+
+# Folder config file
+Desktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+logo.h
+*.autosave
+
+# https://github.com/github/gitignore/blob/master/Global/Tags.gitignore
+# Ignore tags created by etags, ctags, gtags (GNU global) and cscope
+TAGS
+!TAGS/
+tags
+!tags/
+gtags.files
+GTAGS
+GRTAGS
+GPATH
+cscope.files
+cscope.out
+cscope.in.out
+cscope.po.out
+godot.creator.*
+
+projects/
+platform/windows/godot_res.res
+
+# Visual Studio 2017 and Visual Studio Code workspace folder
+/.vs
+/.vscode
+
+# Scons progress indicator
+.scons_node_count
+
+# Godot 4.x
+.godot/
diff --git a/test-project/LICENSE b/test-project/LICENSE
new file mode 100755
index 0000000..f288702
--- /dev/null
+++ b/test-project/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/test-project/README.md b/test-project/README.md
new file mode 100755
index 0000000..a72780a
--- /dev/null
+++ b/test-project/README.md
@@ -0,0 +1,3 @@
+# test-project
+
+Example project used for [godot-ci](https://github.com/aBARICHELLO/godot-ci).
diff --git a/test-project/default_env.tres b/test-project/default_env.tres
new file mode 100644
index 0000000..314b3fa
--- /dev/null
+++ b/test-project/default_env.tres
@@ -0,0 +1,9 @@
+[gd_resource type="Environment" load_steps=2 format=3 uid="uid://be7rgr1r6sv6d"]
+
+[sub_resource type="Sky" id="1"]
+radiance_size = 4
+
+[resource]
+background_mode = 2
+sky = SubResource("1")
+ssao_intensity = 1.0
diff --git a/test-project/export_presets.cfg b/test-project/export_presets.cfg
new file mode 100644
index 0000000..7fd62f7
--- /dev/null
+++ b/test-project/export_presets.cfg
@@ -0,0 +1,980 @@
+[preset.0]
+
+name="Windows Desktop"
+platform="Windows Desktop"
+runnable=true
+advanced_options=false
+dedicated_server=false
+custom_features=""
+export_filter="all_resources"
+include_filter=""
+exclude_filter=""
+export_path=""
+encryption_include_filters=""
+encryption_exclude_filters=""
+encrypt_pck=false
+encrypt_directory=false
+script_export_mode=2
+
+[preset.0.options]
+
+custom_template/debug=""
+custom_template/release=""
+debug/export_console_wrapper=1
+binary_format/embed_pck=false
+texture_format/s3tc_bptc=true
+texture_format/etc2_astc=false
+binary_format/architecture="x86_64"
+codesign/enable=false
+codesign/timestamp=true
+codesign/timestamp_server_url=""
+codesign/digest_algorithm=1
+codesign/description=""
+codesign/custom_options=PackedStringArray()
+application/modify_resources=true
+application/icon=""
+application/console_wrapper_icon=""
+application/icon_interpolation=4
+application/file_version=""
+application/product_version=""
+application/company_name=""
+application/product_name=""
+application/file_description=""
+application/copyright=""
+application/trademarks=""
+application/export_angle=0
+application/export_d3d12=0
+application/d3d12_agility_sdk_multiarch=true
+ssh_remote_deploy/enabled=false
+ssh_remote_deploy/host="user@host_ip"
+ssh_remote_deploy/port="22"
+ssh_remote_deploy/extra_args_ssh=""
+ssh_remote_deploy/extra_args_scp=""
+ssh_remote_deploy/run_script="Expand-Archive -LiteralPath '{temp_dir}\\{archive_name}' -DestinationPath '{temp_dir}'
+$action = New-ScheduledTaskAction -Execute '{temp_dir}\\{exe_name}' -Argument '{cmd_args}'
+$trigger = New-ScheduledTaskTrigger -Once -At 00:00
+$settings = New-ScheduledTaskSettingsSet
+$task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $settings
+Register-ScheduledTask godot_remote_debug -InputObject $task -Force:$true
+Start-ScheduledTask -TaskName godot_remote_debug
+while (Get-ScheduledTask -TaskName godot_remote_debug | ? State -eq running) { Start-Sleep -Milliseconds 100 }
+Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue"
+ssh_remote_deploy/cleanup_script="Stop-ScheduledTask -TaskName godot_remote_debug -ErrorAction:SilentlyContinue
+Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue
+Remove-Item -Recurse -Force '{temp_dir}'"
+texture_format/bptc=true
+texture_format/s3tc=true
+texture_format/etc=true
+texture_format/etc2=true
+texture_format/no_bptc_fallbacks=true
+binary_format/64_bits=true
+
+[preset.1]
+
+name="macOS"
+platform="macOS"
+runnable=true
+advanced_options=false
+dedicated_server=false
+custom_features=""
+export_filter="all_resources"
+include_filter=""
+exclude_filter=""
+export_path=""
+encryption_include_filters=""
+encryption_exclude_filters=""
+encrypt_pck=false
+encrypt_directory=false
+script_export_mode=2
+
+[preset.1.options]
+
+export/distribution_type=1
+binary_format/architecture="universal"
+custom_template/debug=""
+custom_template/release=""
+debug/export_console_wrapper=1
+application/icon=""
+application/icon_interpolation=4
+application/bundle_identifier="org.godotengine.godot-ci"
+application/signature=""
+application/app_category="Games"
+application/short_version=""
+application/version=""
+application/copyright=""
+application/copyright_localized={}
+application/min_macos_version="10.12"
+application/export_angle=0
+display/high_res=true
+application/additional_plist_content=""
+xcode/platform_build="14C18"
+xcode/sdk_version="13.1"
+xcode/sdk_build="22C55"
+xcode/sdk_name="macosx13.1"
+xcode/xcode_version="1420"
+xcode/xcode_build="14C18"
+codesign/codesign=1
+codesign/installer_identity=""
+codesign/apple_team_id=""
+codesign/identity=""
+codesign/entitlements/custom_file=""
+codesign/entitlements/allow_jit_code_execution=false
+codesign/entitlements/allow_unsigned_executable_memory=false
+codesign/entitlements/allow_dyld_environment_variables=false
+codesign/entitlements/disable_library_validation=false
+codesign/entitlements/audio_input=false
+codesign/entitlements/camera=false
+codesign/entitlements/location=false
+codesign/entitlements/address_book=false
+codesign/entitlements/calendars=false
+codesign/entitlements/photos_library=false
+codesign/entitlements/apple_events=false
+codesign/entitlements/debugging=false
+codesign/entitlements/app_sandbox/enabled=false
+codesign/entitlements/app_sandbox/network_server=false
+codesign/entitlements/app_sandbox/network_client=false
+codesign/entitlements/app_sandbox/device_usb=false
+codesign/entitlements/app_sandbox/device_bluetooth=false
+codesign/entitlements/app_sandbox/files_downloads=0
+codesign/entitlements/app_sandbox/files_pictures=0
+codesign/entitlements/app_sandbox/files_music=0
+codesign/entitlements/app_sandbox/files_movies=0
+codesign/entitlements/app_sandbox/files_user_selected=0
+codesign/entitlements/app_sandbox/helper_executables=[]
+codesign/custom_options=PackedStringArray()
+notarization/notarization=0
+privacy/microphone_usage_description=""
+privacy/microphone_usage_description_localized={}
+privacy/camera_usage_description=""
+privacy/camera_usage_description_localized={}
+privacy/location_usage_description=""
+privacy/location_usage_description_localized={}
+privacy/address_book_usage_description=""
+privacy/address_book_usage_description_localized={}
+privacy/calendar_usage_description=""
+privacy/calendar_usage_description_localized={}
+privacy/photos_library_usage_description=""
+privacy/photos_library_usage_description_localized={}
+privacy/desktop_folder_usage_description=""
+privacy/desktop_folder_usage_description_localized={}
+privacy/documents_folder_usage_description=""
+privacy/documents_folder_usage_description_localized={}
+privacy/downloads_folder_usage_description=""
+privacy/downloads_folder_usage_description_localized={}
+privacy/network_volumes_usage_description=""
+privacy/network_volumes_usage_description_localized={}
+privacy/removable_volumes_usage_description=""
+privacy/removable_volumes_usage_description_localized={}
+privacy/tracking_enabled=false
+privacy/tracking_domains=PackedStringArray()
+privacy/collected_data/name/collected=false
+privacy/collected_data/name/linked_to_user=false
+privacy/collected_data/name/used_for_tracking=false
+privacy/collected_data/name/collection_purposes=0
+privacy/collected_data/email_address/collected=false
+privacy/collected_data/email_address/linked_to_user=false
+privacy/collected_data/email_address/used_for_tracking=false
+privacy/collected_data/email_address/collection_purposes=0
+privacy/collected_data/phone_number/collected=false
+privacy/collected_data/phone_number/linked_to_user=false
+privacy/collected_data/phone_number/used_for_tracking=false
+privacy/collected_data/phone_number/collection_purposes=0
+privacy/collected_data/physical_address/collected=false
+privacy/collected_data/physical_address/linked_to_user=false
+privacy/collected_data/physical_address/used_for_tracking=false
+privacy/collected_data/physical_address/collection_purposes=0
+privacy/collected_data/other_contact_info/collected=false
+privacy/collected_data/other_contact_info/linked_to_user=false
+privacy/collected_data/other_contact_info/used_for_tracking=false
+privacy/collected_data/other_contact_info/collection_purposes=0
+privacy/collected_data/health/collected=false
+privacy/collected_data/health/linked_to_user=false
+privacy/collected_data/health/used_for_tracking=false
+privacy/collected_data/health/collection_purposes=0
+privacy/collected_data/fitness/collected=false
+privacy/collected_data/fitness/linked_to_user=false
+privacy/collected_data/fitness/used_for_tracking=false
+privacy/collected_data/fitness/collection_purposes=0
+privacy/collected_data/payment_info/collected=false
+privacy/collected_data/payment_info/linked_to_user=false
+privacy/collected_data/payment_info/used_for_tracking=false
+privacy/collected_data/payment_info/collection_purposes=0
+privacy/collected_data/credit_info/collected=false
+privacy/collected_data/credit_info/linked_to_user=false
+privacy/collected_data/credit_info/used_for_tracking=false
+privacy/collected_data/credit_info/collection_purposes=0
+privacy/collected_data/other_financial_info/collected=false
+privacy/collected_data/other_financial_info/linked_to_user=false
+privacy/collected_data/other_financial_info/used_for_tracking=false
+privacy/collected_data/other_financial_info/collection_purposes=0
+privacy/collected_data/precise_location/collected=false
+privacy/collected_data/precise_location/linked_to_user=false
+privacy/collected_data/precise_location/used_for_tracking=false
+privacy/collected_data/precise_location/collection_purposes=0
+privacy/collected_data/coarse_location/collected=false
+privacy/collected_data/coarse_location/linked_to_user=false
+privacy/collected_data/coarse_location/used_for_tracking=false
+privacy/collected_data/coarse_location/collection_purposes=0
+privacy/collected_data/sensitive_info/collected=false
+privacy/collected_data/sensitive_info/linked_to_user=false
+privacy/collected_data/sensitive_info/used_for_tracking=false
+privacy/collected_data/sensitive_info/collection_purposes=0
+privacy/collected_data/contacts/collected=false
+privacy/collected_data/contacts/linked_to_user=false
+privacy/collected_data/contacts/used_for_tracking=false
+privacy/collected_data/contacts/collection_purposes=0
+privacy/collected_data/emails_or_text_messages/collected=false
+privacy/collected_data/emails_or_text_messages/linked_to_user=false
+privacy/collected_data/emails_or_text_messages/used_for_tracking=false
+privacy/collected_data/emails_or_text_messages/collection_purposes=0
+privacy/collected_data/photos_or_videos/collected=false
+privacy/collected_data/photos_or_videos/linked_to_user=false
+privacy/collected_data/photos_or_videos/used_for_tracking=false
+privacy/collected_data/photos_or_videos/collection_purposes=0
+privacy/collected_data/audio_data/collected=false
+privacy/collected_data/audio_data/linked_to_user=false
+privacy/collected_data/audio_data/used_for_tracking=false
+privacy/collected_data/audio_data/collection_purposes=0
+privacy/collected_data/gameplay_content/collected=false
+privacy/collected_data/gameplay_content/linked_to_user=false
+privacy/collected_data/gameplay_content/used_for_tracking=false
+privacy/collected_data/gameplay_content/collection_purposes=0
+privacy/collected_data/customer_support/collected=false
+privacy/collected_data/customer_support/linked_to_user=false
+privacy/collected_data/customer_support/used_for_tracking=false
+privacy/collected_data/customer_support/collection_purposes=0
+privacy/collected_data/other_user_content/collected=false
+privacy/collected_data/other_user_content/linked_to_user=false
+privacy/collected_data/other_user_content/used_for_tracking=false
+privacy/collected_data/other_user_content/collection_purposes=0
+privacy/collected_data/browsing_history/collected=false
+privacy/collected_data/browsing_history/linked_to_user=false
+privacy/collected_data/browsing_history/used_for_tracking=false
+privacy/collected_data/browsing_history/collection_purposes=0
+privacy/collected_data/search_hhistory/collected=false
+privacy/collected_data/search_hhistory/linked_to_user=false
+privacy/collected_data/search_hhistory/used_for_tracking=false
+privacy/collected_data/search_hhistory/collection_purposes=0
+privacy/collected_data/user_id/collected=false
+privacy/collected_data/user_id/linked_to_user=false
+privacy/collected_data/user_id/used_for_tracking=false
+privacy/collected_data/user_id/collection_purposes=0
+privacy/collected_data/device_id/collected=false
+privacy/collected_data/device_id/linked_to_user=false
+privacy/collected_data/device_id/used_for_tracking=false
+privacy/collected_data/device_id/collection_purposes=0
+privacy/collected_data/purchase_history/collected=false
+privacy/collected_data/purchase_history/linked_to_user=false
+privacy/collected_data/purchase_history/used_for_tracking=false
+privacy/collected_data/purchase_history/collection_purposes=0
+privacy/collected_data/product_interaction/collected=false
+privacy/collected_data/product_interaction/linked_to_user=false
+privacy/collected_data/product_interaction/used_for_tracking=false
+privacy/collected_data/product_interaction/collection_purposes=0
+privacy/collected_data/advertising_data/collected=false
+privacy/collected_data/advertising_data/linked_to_user=false
+privacy/collected_data/advertising_data/used_for_tracking=false
+privacy/collected_data/advertising_data/collection_purposes=0
+privacy/collected_data/other_usage_data/collected=false
+privacy/collected_data/other_usage_data/linked_to_user=false
+privacy/collected_data/other_usage_data/used_for_tracking=false
+privacy/collected_data/other_usage_data/collection_purposes=0
+privacy/collected_data/crash_data/collected=false
+privacy/collected_data/crash_data/linked_to_user=false
+privacy/collected_data/crash_data/used_for_tracking=false
+privacy/collected_data/crash_data/collection_purposes=0
+privacy/collected_data/performance_data/collected=false
+privacy/collected_data/performance_data/linked_to_user=false
+privacy/collected_data/performance_data/used_for_tracking=false
+privacy/collected_data/performance_data/collection_purposes=0
+privacy/collected_data/other_diagnostic_data/collected=false
+privacy/collected_data/other_diagnostic_data/linked_to_user=false
+privacy/collected_data/other_diagnostic_data/used_for_tracking=false
+privacy/collected_data/other_diagnostic_data/collection_purposes=0
+privacy/collected_data/environment_scanning/collected=false
+privacy/collected_data/environment_scanning/linked_to_user=false
+privacy/collected_data/environment_scanning/used_for_tracking=false
+privacy/collected_data/environment_scanning/collection_purposes=0
+privacy/collected_data/hands/collected=false
+privacy/collected_data/hands/linked_to_user=false
+privacy/collected_data/hands/used_for_tracking=false
+privacy/collected_data/hands/collection_purposes=0
+privacy/collected_data/head/collected=false
+privacy/collected_data/head/linked_to_user=false
+privacy/collected_data/head/used_for_tracking=false
+privacy/collected_data/head/collection_purposes=0
+privacy/collected_data/other_data_types/collected=false
+privacy/collected_data/other_data_types/linked_to_user=false
+privacy/collected_data/other_data_types/used_for_tracking=false
+privacy/collected_data/other_data_types/collection_purposes=0
+ssh_remote_deploy/enabled=false
+ssh_remote_deploy/host="user@host_ip"
+ssh_remote_deploy/port="22"
+ssh_remote_deploy/extra_args_ssh=""
+ssh_remote_deploy/extra_args_scp=""
+ssh_remote_deploy/run_script="#!/usr/bin/env bash
+unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\"
+open \"{temp_dir}/{exe_name}.app\" --args {cmd_args}"
+ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash
+kill $(pgrep -x -f \"{temp_dir}/{exe_name}.app/Contents/MacOS/{exe_name} {cmd_args}\")
+rm -rf \"{temp_dir}\""
+graphics/32_bits_framebuffer=true
+xr_features/xr_mode=0
+xr_features/degrees_of_freedom=0
+xr_features/hand_tracking=0
+xr_features/focus_awareness=false
+one_click_deploy/clear_previous_install=false
+custom_template/use_custom_build=false
+command_line/extra_args=""
+version/code=1
+version/name="1.0"
+package/unique_name="org.godotengine.$genname"
+package/name=""
+package/signed=true
+screen/immersive_mode=true
+screen/orientation=0
+screen/support_small=true
+screen/support_normal=true
+screen/support_large=true
+screen/support_xlarge=true
+screen/opengl_debug=false
+launcher_icons/main_192x192=""
+launcher_icons/adaptive_foreground_432x432=""
+launcher_icons/adaptive_background_432x432=""
+keystore/debug=""
+keystore/debug_user=""
+keystore/debug_password=""
+keystore/release=""
+keystore/release_user=""
+keystore/release_password=""
+apk_expansion/enable=false
+apk_expansion/SALT=""
+apk_expansion/public_key=""
+architectures/armeabi-v7a=true
+architectures/arm64-v8a=true
+architectures/x86=false
+architectures/x86_64=false
+permissions/custom_permissions=PackedStringArray()
+permissions/access_checkin_properties=false
+permissions/access_coarse_location=false
+permissions/access_fine_location=false
+permissions/access_location_extra_commands=false
+permissions/access_mock_location=false
+permissions/access_network_state=false
+permissions/access_surface_flinger=false
+permissions/access_wifi_state=false
+permissions/account_manager=false
+permissions/add_voicemail=false
+permissions/authenticate_accounts=false
+permissions/battery_stats=false
+permissions/bind_accessibility_service=false
+permissions/bind_appwidget=false
+permissions/bind_device_admin=false
+permissions/bind_input_method=false
+permissions/bind_nfc_service=false
+permissions/bind_notification_listener_service=false
+permissions/bind_print_service=false
+permissions/bind_remoteviews=false
+permissions/bind_text_service=false
+permissions/bind_vpn_service=false
+permissions/bind_wallpaper=false
+permissions/bluetooth=false
+permissions/bluetooth_admin=false
+permissions/bluetooth_privileged=false
+permissions/brick=false
+permissions/broadcast_package_removed=false
+permissions/broadcast_sms=false
+permissions/broadcast_sticky=false
+permissions/broadcast_wap_push=false
+permissions/call_phone=false
+permissions/call_privileged=false
+permissions/camera=false
+permissions/capture_audio_output=false
+permissions/capture_secure_video_output=false
+permissions/capture_video_output=false
+permissions/change_component_enabled_state=false
+permissions/change_configuration=false
+permissions/change_network_state=false
+permissions/change_wifi_multicast_state=false
+permissions/change_wifi_state=false
+permissions/clear_app_cache=false
+permissions/clear_app_user_data=false
+permissions/control_location_updates=false
+permissions/delete_cache_files=false
+permissions/delete_packages=false
+permissions/device_power=false
+permissions/diagnostic=false
+permissions/disable_keyguard=false
+permissions/dump=false
+permissions/expand_status_bar=false
+permissions/factory_test=false
+permissions/flashlight=false
+permissions/force_back=false
+permissions/get_accounts=false
+permissions/get_package_size=false
+permissions/get_tasks=false
+permissions/get_top_activity_info=false
+permissions/global_search=false
+permissions/hardware_test=false
+permissions/inject_events=false
+permissions/install_location_provider=false
+permissions/install_packages=false
+permissions/install_shortcut=false
+permissions/internal_system_window=false
+permissions/internet=false
+permissions/kill_background_processes=false
+permissions/location_hardware=false
+permissions/manage_accounts=false
+permissions/manage_app_tokens=false
+permissions/manage_documents=false
+permissions/master_clear=false
+permissions/media_content_control=false
+permissions/modify_audio_settings=false
+permissions/modify_phone_state=false
+permissions/mount_format_filesystems=false
+permissions/mount_unmount_filesystems=false
+permissions/nfc=false
+permissions/persistent_activity=false
+permissions/process_outgoing_calls=false
+permissions/read_calendar=false
+permissions/read_call_log=false
+permissions/read_contacts=false
+permissions/read_external_storage=false
+permissions/read_frame_buffer=false
+permissions/read_history_bookmarks=false
+permissions/read_input_state=false
+permissions/read_logs=false
+permissions/read_phone_state=false
+permissions/read_profile=false
+permissions/read_sms=false
+permissions/read_social_stream=false
+permissions/read_sync_settings=false
+permissions/read_sync_stats=false
+permissions/read_user_dictionary=false
+permissions/reboot=false
+permissions/receive_boot_completed=false
+permissions/receive_mms=false
+permissions/receive_sms=false
+permissions/receive_wap_push=false
+permissions/record_audio=false
+permissions/reorder_tasks=false
+permissions/restart_packages=false
+permissions/send_respond_via_message=false
+permissions/send_sms=false
+permissions/set_activity_watcher=false
+permissions/set_alarm=false
+permissions/set_always_finish=false
+permissions/set_animation_scale=false
+permissions/set_debug_app=false
+permissions/set_orientation=false
+permissions/set_pointer_speed=false
+permissions/set_preferred_applications=false
+permissions/set_process_limit=false
+permissions/set_time=false
+permissions/set_time_zone=false
+permissions/set_wallpaper=false
+permissions/set_wallpaper_hints=false
+permissions/signal_persistent_processes=false
+permissions/status_bar=false
+permissions/subscribed_feeds_read=false
+permissions/subscribed_feeds_write=false
+permissions/system_alert_window=false
+permissions/transmit_ir=false
+permissions/uninstall_shortcut=false
+permissions/update_device_stats=false
+permissions/use_credentials=false
+permissions/use_sip=false
+permissions/vibrate=false
+permissions/wake_lock=false
+permissions/write_apn_settings=false
+permissions/write_calendar=false
+permissions/write_call_log=false
+permissions/write_contacts=false
+permissions/write_external_storage=false
+permissions/write_gservices=false
+permissions/write_history_bookmarks=false
+permissions/write_profile=false
+permissions/write_secure_settings=false
+permissions/write_settings=false
+permissions/write_sms=false
+permissions/write_social_stream=false
+permissions/write_sync_settings=false
+permissions/write_user_dictionary=false
+
+[preset.2]
+
+name="Linux/X11"
+platform="Linux"
+runnable=true
+advanced_options=false
+dedicated_server=false
+custom_features=""
+export_filter="all_resources"
+include_filter=""
+exclude_filter=""
+export_path=""
+encryption_include_filters=""
+encryption_exclude_filters=""
+encrypt_pck=false
+encrypt_directory=false
+script_export_mode=2
+
+[preset.2.options]
+
+custom_template/debug=""
+custom_template/release=""
+debug/export_console_wrapper=1
+binary_format/embed_pck=false
+texture_format/s3tc_bptc=true
+texture_format/etc2_astc=false
+binary_format/architecture="x86_64"
+ssh_remote_deploy/enabled=false
+ssh_remote_deploy/host="user@host_ip"
+ssh_remote_deploy/port="22"
+ssh_remote_deploy/extra_args_ssh=""
+ssh_remote_deploy/extra_args_scp=""
+ssh_remote_deploy/run_script="#!/usr/bin/env bash
+export DISPLAY=:0
+unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\"
+\"{temp_dir}/{exe_name}\" {cmd_args}"
+ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash
+kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\")
+rm -rf \"{temp_dir}\""
+texture_format/bptc=true
+texture_format/s3tc=true
+texture_format/etc=true
+texture_format/etc2=true
+texture_format/no_bptc_fallbacks=true
+binary_format/64_bits=true
+
+[preset.3]
+
+name="Web"
+platform="Web"
+runnable=true
+advanced_options=false
+dedicated_server=false
+custom_features=""
+export_filter="all_resources"
+include_filter=""
+exclude_filter=""
+export_path=""
+encryption_include_filters=""
+encryption_exclude_filters=""
+encrypt_pck=false
+encrypt_directory=false
+script_export_mode=2
+
+[preset.3.options]
+
+custom_template/debug=""
+custom_template/release=""
+variant/extensions_support=false
+variant/thread_support=false
+vram_texture_compression/for_desktop=true
+vram_texture_compression/for_mobile=false
+html/export_icon=true
+html/custom_html_shell=""
+html/head_include=""
+html/canvas_resize_policy=2
+html/focus_canvas_on_start=true
+html/experimental_virtual_keyboard=false
+progressive_web_app/enabled=false
+progressive_web_app/ensure_cross_origin_isolation_headers=true
+progressive_web_app/offline_page=""
+progressive_web_app/display=1
+progressive_web_app/orientation=0
+progressive_web_app/icon_144x144=""
+progressive_web_app/icon_180x180=""
+progressive_web_app/icon_512x512=""
+progressive_web_app/background_color=Color(0, 0, 0, 1)
+graphics/32_bits_framebuffer=true
+xr_features/xr_mode=0
+xr_features/degrees_of_freedom=0
+xr_features/hand_tracking=0
+xr_features/focus_awareness=false
+one_click_deploy/clear_previous_install=false
+custom_template/use_custom_build=false
+command_line/extra_args=""
+version/code=1
+version/name="1.0"
+package/unique_name="org.godotengine.$genname"
+package/name=""
+package/signed=true
+screen/immersive_mode=true
+screen/orientation=0
+screen/support_small=true
+screen/support_normal=true
+screen/support_large=true
+screen/support_xlarge=true
+screen/opengl_debug=false
+launcher_icons/main_192x192=""
+launcher_icons/adaptive_foreground_432x432=""
+launcher_icons/adaptive_background_432x432=""
+keystore/debug=""
+keystore/debug_user=""
+keystore/debug_password=""
+keystore/release=""
+keystore/release_user=""
+keystore/release_password=""
+apk_expansion/enable=false
+apk_expansion/SALT=""
+apk_expansion/public_key=""
+architectures/armeabi-v7a=true
+architectures/arm64-v8a=true
+architectures/x86=false
+architectures/x86_64=false
+permissions/custom_permissions=PackedStringArray()
+permissions/access_checkin_properties=false
+permissions/access_coarse_location=false
+permissions/access_fine_location=false
+permissions/access_location_extra_commands=false
+permissions/access_mock_location=false
+permissions/access_network_state=false
+permissions/access_surface_flinger=false
+permissions/access_wifi_state=false
+permissions/account_manager=false
+permissions/add_voicemail=false
+permissions/authenticate_accounts=false
+permissions/battery_stats=false
+permissions/bind_accessibility_service=false
+permissions/bind_appwidget=false
+permissions/bind_device_admin=false
+permissions/bind_input_method=false
+permissions/bind_nfc_service=false
+permissions/bind_notification_listener_service=false
+permissions/bind_print_service=false
+permissions/bind_remoteviews=false
+permissions/bind_text_service=false
+permissions/bind_vpn_service=false
+permissions/bind_wallpaper=false
+permissions/bluetooth=false
+permissions/bluetooth_admin=false
+permissions/bluetooth_privileged=false
+permissions/brick=false
+permissions/broadcast_package_removed=false
+permissions/broadcast_sms=false
+permissions/broadcast_sticky=false
+permissions/broadcast_wap_push=false
+permissions/call_phone=false
+permissions/call_privileged=false
+permissions/camera=false
+permissions/capture_audio_output=false
+permissions/capture_secure_video_output=false
+permissions/capture_video_output=false
+permissions/change_component_enabled_state=false
+permissions/change_configuration=false
+permissions/change_network_state=false
+permissions/change_wifi_multicast_state=false
+permissions/change_wifi_state=false
+permissions/clear_app_cache=false
+permissions/clear_app_user_data=false
+permissions/control_location_updates=false
+permissions/delete_cache_files=false
+permissions/delete_packages=false
+permissions/device_power=false
+permissions/diagnostic=false
+permissions/disable_keyguard=false
+permissions/dump=false
+permissions/expand_status_bar=false
+permissions/factory_test=false
+permissions/flashlight=false
+permissions/force_back=false
+permissions/get_accounts=false
+permissions/get_package_size=false
+permissions/get_tasks=false
+permissions/get_top_activity_info=false
+permissions/global_search=false
+permissions/hardware_test=false
+permissions/inject_events=false
+permissions/install_location_provider=false
+permissions/install_packages=false
+permissions/install_shortcut=false
+permissions/internal_system_window=false
+permissions/internet=false
+permissions/kill_background_processes=false
+permissions/location_hardware=false
+permissions/manage_accounts=false
+permissions/manage_app_tokens=false
+permissions/manage_documents=false
+permissions/master_clear=false
+permissions/media_content_control=false
+permissions/modify_audio_settings=false
+permissions/modify_phone_state=false
+permissions/mount_format_filesystems=false
+permissions/mount_unmount_filesystems=false
+permissions/nfc=false
+permissions/persistent_activity=false
+permissions/process_outgoing_calls=false
+permissions/read_calendar=false
+permissions/read_call_log=false
+permissions/read_contacts=false
+permissions/read_external_storage=false
+permissions/read_frame_buffer=false
+permissions/read_history_bookmarks=false
+permissions/read_input_state=false
+permissions/read_logs=false
+permissions/read_phone_state=false
+permissions/read_profile=false
+permissions/read_sms=false
+permissions/read_social_stream=false
+permissions/read_sync_settings=false
+permissions/read_sync_stats=false
+permissions/read_user_dictionary=false
+permissions/reboot=false
+permissions/receive_boot_completed=false
+permissions/receive_mms=false
+permissions/receive_sms=false
+permissions/receive_wap_push=false
+permissions/record_audio=false
+permissions/reorder_tasks=false
+permissions/restart_packages=false
+permissions/send_respond_via_message=false
+permissions/send_sms=false
+permissions/set_activity_watcher=false
+permissions/set_alarm=false
+permissions/set_always_finish=false
+permissions/set_animation_scale=false
+permissions/set_debug_app=false
+permissions/set_orientation=false
+permissions/set_pointer_speed=false
+permissions/set_preferred_applications=false
+permissions/set_process_limit=false
+permissions/set_time=false
+permissions/set_time_zone=false
+permissions/set_wallpaper=false
+permissions/set_wallpaper_hints=false
+permissions/signal_persistent_processes=false
+permissions/status_bar=false
+permissions/subscribed_feeds_read=false
+permissions/subscribed_feeds_write=false
+permissions/system_alert_window=false
+permissions/transmit_ir=false
+permissions/uninstall_shortcut=false
+permissions/update_device_stats=false
+permissions/use_credentials=false
+permissions/use_sip=false
+permissions/vibrate=false
+permissions/wake_lock=false
+permissions/write_apn_settings=false
+permissions/write_calendar=false
+permissions/write_call_log=false
+permissions/write_contacts=false
+permissions/write_external_storage=false
+permissions/write_gservices=false
+permissions/write_history_bookmarks=false
+permissions/write_profile=false
+permissions/write_secure_settings=false
+permissions/write_settings=false
+permissions/write_sms=false
+permissions/write_social_stream=false
+permissions/write_sync_settings=false
+permissions/write_user_dictionary=false
+
+[preset.4]
+
+name="Android"
+platform="Android"
+runnable=true
+advanced_options=false
+dedicated_server=false
+custom_features=""
+export_filter="all_resources"
+include_filter=""
+exclude_filter=""
+export_path=""
+encryption_include_filters=""
+encryption_exclude_filters=""
+encrypt_pck=false
+encrypt_directory=false
+script_export_mode=2
+
+[preset.4.options]
+
+custom_template/debug=""
+custom_template/release=""
+gradle_build/use_gradle_build=false
+gradle_build/gradle_build_directory=""
+gradle_build/android_source_template=""
+gradle_build/compress_native_libraries=false
+gradle_build/export_format=0
+gradle_build/min_sdk=""
+gradle_build/target_sdk=""
+architectures/armeabi-v7a=false
+architectures/arm64-v8a=true
+architectures/x86=false
+architectures/x86_64=false
+version/code=1
+version/name=""
+package/unique_name="com.example.$genname"
+package/name=""
+package/signed=true
+package/app_category=2
+package/retain_data_on_uninstall=false
+package/exclude_from_recents=false
+package/show_in_android_tv=false
+package/show_in_app_library=true
+package/show_as_launcher_app=false
+launcher_icons/main_192x192=""
+launcher_icons/adaptive_foreground_432x432=""
+launcher_icons/adaptive_background_432x432=""
+graphics/opengl_debug=false
+xr_features/xr_mode=0
+screen/immersive_mode=true
+screen/support_small=true
+screen/support_normal=true
+screen/support_large=true
+screen/support_xlarge=true
+user_data_backup/allow=false
+command_line/extra_args=""
+apk_expansion/enable=false
+apk_expansion/SALT=""
+apk_expansion/public_key=""
+permissions/custom_permissions=PackedStringArray()
+permissions/access_checkin_properties=false
+permissions/access_coarse_location=false
+permissions/access_fine_location=false
+permissions/access_location_extra_commands=false
+permissions/access_mock_location=false
+permissions/access_network_state=false
+permissions/access_surface_flinger=false
+permissions/access_wifi_state=false
+permissions/account_manager=false
+permissions/add_voicemail=false
+permissions/authenticate_accounts=false
+permissions/battery_stats=false
+permissions/bind_accessibility_service=false
+permissions/bind_appwidget=false
+permissions/bind_device_admin=false
+permissions/bind_input_method=false
+permissions/bind_nfc_service=false
+permissions/bind_notification_listener_service=false
+permissions/bind_print_service=false
+permissions/bind_remoteviews=false
+permissions/bind_text_service=false
+permissions/bind_vpn_service=false
+permissions/bind_wallpaper=false
+permissions/bluetooth=false
+permissions/bluetooth_admin=false
+permissions/bluetooth_privileged=false
+permissions/brick=false
+permissions/broadcast_package_removed=false
+permissions/broadcast_sms=false
+permissions/broadcast_sticky=false
+permissions/broadcast_wap_push=false
+permissions/call_phone=false
+permissions/call_privileged=false
+permissions/camera=false
+permissions/capture_audio_output=false
+permissions/capture_secure_video_output=false
+permissions/capture_video_output=false
+permissions/change_component_enabled_state=false
+permissions/change_configuration=false
+permissions/change_network_state=false
+permissions/change_wifi_multicast_state=false
+permissions/change_wifi_state=false
+permissions/clear_app_cache=false
+permissions/clear_app_user_data=false
+permissions/control_location_updates=false
+permissions/delete_cache_files=false
+permissions/delete_packages=false
+permissions/device_power=false
+permissions/diagnostic=false
+permissions/disable_keyguard=false
+permissions/dump=false
+permissions/expand_status_bar=false
+permissions/factory_test=false
+permissions/flashlight=false
+permissions/force_back=false
+permissions/get_accounts=false
+permissions/get_package_size=false
+permissions/get_tasks=false
+permissions/get_top_activity_info=false
+permissions/global_search=false
+permissions/hardware_test=false
+permissions/inject_events=false
+permissions/install_location_provider=false
+permissions/install_packages=false
+permissions/install_shortcut=false
+permissions/internal_system_window=false
+permissions/internet=false
+permissions/kill_background_processes=false
+permissions/location_hardware=false
+permissions/manage_accounts=false
+permissions/manage_app_tokens=false
+permissions/manage_documents=false
+permissions/manage_external_storage=false
+permissions/master_clear=false
+permissions/media_content_control=false
+permissions/modify_audio_settings=false
+permissions/modify_phone_state=false
+permissions/mount_format_filesystems=false
+permissions/mount_unmount_filesystems=false
+permissions/nfc=false
+permissions/persistent_activity=false
+permissions/post_notifications=false
+permissions/process_outgoing_calls=false
+permissions/read_calendar=false
+permissions/read_call_log=false
+permissions/read_contacts=false
+permissions/read_external_storage=false
+permissions/read_frame_buffer=false
+permissions/read_history_bookmarks=false
+permissions/read_input_state=false
+permissions/read_logs=false
+permissions/read_phone_state=false
+permissions/read_profile=false
+permissions/read_sms=false
+permissions/read_social_stream=false
+permissions/read_sync_settings=false
+permissions/read_sync_stats=false
+permissions/read_user_dictionary=false
+permissions/reboot=false
+permissions/receive_boot_completed=false
+permissions/receive_mms=false
+permissions/receive_sms=false
+permissions/receive_wap_push=false
+permissions/record_audio=false
+permissions/reorder_tasks=false
+permissions/restart_packages=false
+permissions/send_respond_via_message=false
+permissions/send_sms=false
+permissions/set_activity_watcher=false
+permissions/set_alarm=false
+permissions/set_always_finish=false
+permissions/set_animation_scale=false
+permissions/set_debug_app=false
+permissions/set_orientation=false
+permissions/set_pointer_speed=false
+permissions/set_preferred_applications=false
+permissions/set_process_limit=false
+permissions/set_time=false
+permissions/set_time_zone=false
+permissions/set_wallpaper=false
+permissions/set_wallpaper_hints=false
+permissions/signal_persistent_processes=false
+permissions/status_bar=false
+permissions/subscribed_feeds_read=false
+permissions/subscribed_feeds_write=false
+permissions/system_alert_window=false
+permissions/transmit_ir=false
+permissions/uninstall_shortcut=false
+permissions/update_device_stats=false
+permissions/use_credentials=false
+permissions/use_sip=false
+permissions/vibrate=false
+permissions/wake_lock=false
+permissions/write_apn_settings=false
+permissions/write_calendar=false
+permissions/write_call_log=false
+permissions/write_contacts=false
+permissions/write_external_storage=false
+permissions/write_gservices=false
+permissions/write_history_bookmarks=false
+permissions/write_profile=false
+permissions/write_secure_settings=false
+permissions/write_settings=false
+permissions/write_sms=false
+permissions/write_social_stream=false
+permissions/write_sync_settings=false
+permissions/write_user_dictionary=false
diff --git a/test-project/icon.png b/test-project/icon.png
new file mode 100755
index 0000000..a0b64ee
Binary files /dev/null and b/test-project/icon.png differ
diff --git a/test-project/icon.png.import b/test-project/icon.png.import
new file mode 100644
index 0000000..3cb7db5
--- /dev/null
+++ b/test-project/icon.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://pkdvktqcxe0v"
+path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://icon.png"
+dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/test-project/project.godot b/test-project/project.godot
new file mode 100644
index 0000000..adbf0bd
--- /dev/null
+++ b/test-project/project.godot
@@ -0,0 +1,60 @@
+; Engine configuration file.
+; It's best edited using the editor UI and not directly,
+; since the parameters that go here are not all obvious.
+;
+; Format:
+; [section] ; section goes between []
+; param=value ; assign values to parameters
+
+config_version=5
+
+[application]
+
+config/name="gameoff"
+run/main_scene="res://scenes/Main.tscn"
+config/features=PackedStringArray("4.3")
+config/icon="res://icon.png"
+
+[autoload]
+
+GLOBAL="*res://scripts/GLOBAL.gd"
+
+[display]
+
+window/size/viewport_width=1920
+window/size/viewport_height=1080
+window/size/resizable=false
+
+[input]
+
+ui_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":4194320,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
+, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":11,"pressure":0.0,"pressed":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":87,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
+]
+}
+ui_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":4194322,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
+, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":12,"pressure":0.0,"pressed":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":83,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
+]
+}
+swap={
+"deadzone": 0.5,
+"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":2,"canceled":false,"pressed":false,"double_click":false,"script":null)
+]
+}
+fire={
+"deadzone": 0.5,
+"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"double_click":false,"script":null)
+]
+}
+
+[rendering]
+
+textures/vram_compression/import_etc2_astc=true
+environment/defaults/default_environment="res://default_env.tres"
+quality/driver/driver_name="GLES2"
+vram_compression/import_etc=true
diff --git a/test-project/resources/gimp/1.xcf b/test-project/resources/gimp/1.xcf
new file mode 100755
index 0000000..70ed45e
Binary files /dev/null and b/test-project/resources/gimp/1.xcf differ
diff --git a/test-project/resources/gimp/2.xcf b/test-project/resources/gimp/2.xcf
new file mode 100755
index 0000000..217c644
Binary files /dev/null and b/test-project/resources/gimp/2.xcf differ
diff --git a/test-project/resources/gimp/cat0.xcf b/test-project/resources/gimp/cat0.xcf
new file mode 100755
index 0000000..94b68da
Binary files /dev/null and b/test-project/resources/gimp/cat0.xcf differ
diff --git a/test-project/resources/img/1.png b/test-project/resources/img/1.png
new file mode 100755
index 0000000..f01e14a
Binary files /dev/null and b/test-project/resources/img/1.png differ
diff --git a/test-project/resources/img/1.png.import b/test-project/resources/img/1.png.import
new file mode 100644
index 0000000..8acb8b1
--- /dev/null
+++ b/test-project/resources/img/1.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://c13lokwvlumq"
+path="res://.godot/imported/1.png-874a0dfc3e148ca5b5c7b731b459b9a9.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://resources/img/1.png"
+dest_files=["res://.godot/imported/1.png-874a0dfc3e148ca5b5c7b731b459b9a9.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/test-project/resources/img/2.png b/test-project/resources/img/2.png
new file mode 100755
index 0000000..3363ba1
Binary files /dev/null and b/test-project/resources/img/2.png differ
diff --git a/test-project/resources/img/2.png.import b/test-project/resources/img/2.png.import
new file mode 100644
index 0000000..d030c08
--- /dev/null
+++ b/test-project/resources/img/2.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://sss8hj0itmbm"
+path="res://.godot/imported/2.png-a7dea376fea94ea1eb370fce6b063bfe.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://resources/img/2.png"
+dest_files=["res://.godot/imported/2.png-a7dea376fea94ea1eb370fce6b063bfe.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/test-project/resources/img/bullet-placeholder.png b/test-project/resources/img/bullet-placeholder.png
new file mode 100755
index 0000000..a0d8a03
Binary files /dev/null and b/test-project/resources/img/bullet-placeholder.png differ
diff --git a/test-project/resources/img/bullet-placeholder.png.import b/test-project/resources/img/bullet-placeholder.png.import
new file mode 100644
index 0000000..f09ba4c
--- /dev/null
+++ b/test-project/resources/img/bullet-placeholder.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://us5yc614v74l"
+path="res://.godot/imported/bullet-placeholder.png-eeaa4de234c0486ca1a9eb8dc6789c56.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://resources/img/bullet-placeholder.png"
+dest_files=["res://.godot/imported/bullet-placeholder.png-eeaa4de234c0486ca1a9eb8dc6789c56.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/test-project/resources/img/cat0.png b/test-project/resources/img/cat0.png
new file mode 100755
index 0000000..1372d09
Binary files /dev/null and b/test-project/resources/img/cat0.png differ
diff --git a/test-project/resources/img/cat0.png.import b/test-project/resources/img/cat0.png.import
new file mode 100644
index 0000000..3831a00
--- /dev/null
+++ b/test-project/resources/img/cat0.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://b6au28glxf586"
+path="res://.godot/imported/cat0.png-c2a0d2145a7b7ae5da0c2cedfce916ba.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://resources/img/cat0.png"
+dest_files=["res://.godot/imported/cat0.png-c2a0d2145a7b7ae5da0c2cedfce916ba.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/test-project/resources/img/coil.png b/test-project/resources/img/coil.png
new file mode 100755
index 0000000..c65f703
Binary files /dev/null and b/test-project/resources/img/coil.png differ
diff --git a/test-project/resources/img/coil.png.import b/test-project/resources/img/coil.png.import
new file mode 100644
index 0000000..ced83d6
--- /dev/null
+++ b/test-project/resources/img/coil.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bkdumwaxj31xo"
+path="res://.godot/imported/coil.png-9a3ed0a2a28db47534112a3d0aada371.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://resources/img/coil.png"
+dest_files=["res://.godot/imported/coil.png-9a3ed0a2a28db47534112a3d0aada371.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/test-project/resources/img/missile-placeholder.png b/test-project/resources/img/missile-placeholder.png
new file mode 100755
index 0000000..54739c8
Binary files /dev/null and b/test-project/resources/img/missile-placeholder.png differ
diff --git a/test-project/resources/img/missile-placeholder.png.import b/test-project/resources/img/missile-placeholder.png.import
new file mode 100644
index 0000000..f629d37
--- /dev/null
+++ b/test-project/resources/img/missile-placeholder.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://my3jd38hyc7k"
+path="res://.godot/imported/missile-placeholder.png-1d86beeaaa1c40b1fbc4c2a8d00be077.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://resources/img/missile-placeholder.png"
+dest_files=["res://.godot/imported/missile-placeholder.png-1d86beeaaa1c40b1fbc4c2a8d00be077.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/test-project/scenes/Arena.tscn b/test-project/scenes/Arena.tscn
new file mode 100755
index 0000000..4960c8d
--- /dev/null
+++ b/test-project/scenes/Arena.tscn
@@ -0,0 +1,10 @@
+[gd_scene load_steps=3 format=2]
+
+[ext_resource path="res://scripts/Arena.gd" type="Script" id=1]
+[ext_resource path="res://scenes/Background.tscn" type="PackedScene" id=2]
+
+[node name="Arena" type="Node2D"]
+script = ExtResource( 1 )
+
+[node name="Background" parent="." instance=ExtResource( 2 )]
+
diff --git a/test-project/scenes/Background.tscn b/test-project/scenes/Background.tscn
new file mode 100755
index 0000000..e9b1292
--- /dev/null
+++ b/test-project/scenes/Background.tscn
@@ -0,0 +1,28 @@
+[gd_scene load_steps=3 format=2]
+
+[ext_resource path="res://scripts/ColoredEntity.gd" type="Script" id=1]
+[ext_resource path="res://resources/img/2.png" type="Texture2D" id=2]
+
+[node name="Background" type="Node2D" groups=[
+"ColoredEntity",
+]]
+script = ExtResource( 1 )
+
+[node name="NinePatchRect" type="TextureRect" parent="."]
+anchor_left = 0.0
+anchor_top = 0.0
+anchor_right = 0.0
+anchor_bottom = 0.0
+offset_left = -199.0
+offset_top = -157.0
+offset_right = 2097.0
+offset_bottom = 1217.0
+pivot_offset = Vector2( 0, 0 )
+clip_contents = false
+mouse_filter = 1
+mouse_default_cursor_shape = 0
+size_flags_horizontal = 1
+size_flags_vertical = 1
+texture = ExtResource( 2 )
+stretch_mode = 2
+
diff --git a/test-project/scenes/Base.tscn b/test-project/scenes/Base.tscn
new file mode 100755
index 0000000..46a2427
--- /dev/null
+++ b/test-project/scenes/Base.tscn
@@ -0,0 +1,46 @@
+[gd_scene load_steps=3 format=2]
+
+[ext_resource path="res://scripts/Base.gd" type="Script" id=1]
+
+[sub_resource type="CircleShape2D" id=1]
+
+custom_solver_bias = 0.0
+radius = 68.9104
+
+[node name="Base" type="Node2D" groups=[
+"ColoredEntity",
+]]
+script = ExtResource( 1 )
+
+[node name="ColorRect" type="ColorRect" parent="."]
+anchor_left = 0.5
+anchor_top = 0.5
+anchor_right = 0.5
+anchor_bottom = 0.5
+offset_left = -60.5
+offset_top = -33.0
+offset_right = 60.5
+offset_bottom = 33.0
+pivot_offset = Vector2( 0, 0 )
+clip_contents = false
+mouse_filter = 0
+mouse_default_cursor_shape = 0
+size_flags_horizontal = 1
+size_flags_vertical = 1
+color = Color( 1, 1, 1, 1 )
+
+[node name="Body" type="Area2D" parent="."]
+input_pickable = true
+gravity_direction = Vector2( 0, 1 )
+gravity = 98.0
+linear_damp = 0.1
+angular_damp = 1.0
+collision_layer = 12
+collision_mask = 12
+audio_bus_override = false
+audio_bus_name = "Master"
+
+[node name="CollisionMask" type="CollisionShape2D" parent="Body"]
+shape = SubResource( 1 )
+
+[connection signal="body_entered" from="Body" to="." method="_on_Body_body_entered"]
diff --git a/test-project/scenes/Cannon.tscn b/test-project/scenes/Cannon.tscn
new file mode 100755
index 0000000..c193108
--- /dev/null
+++ b/test-project/scenes/Cannon.tscn
@@ -0,0 +1,54 @@
+[gd_scene load_steps=3 format=2]
+
+[ext_resource path="res://scripts/Cannon.gd" type="Script" id=1]
+[ext_resource path="res://resources/img/1.png" type="Texture2D" id=2]
+
+[node name="Cannon" type="Node2D" groups=[
+"ColoredEntity",
+]]
+script = ExtResource( 1 )
+
+[node name="Sprite2D" type="TextureRect" parent="."]
+anchor_left = 0.0
+anchor_top = 0.5
+anchor_right = 0.0
+anchor_bottom = 0.5
+offset_top = -10.0
+offset_right = 150.0
+offset_bottom = 38.0
+custom_minimum_size = Vector2( 150, 24 )
+pivot_offset = Vector2( 10, 10 )
+clip_contents = false
+mouse_filter = 1
+mouse_default_cursor_shape = 0
+size_flags_horizontal = 0
+size_flags_vertical = 0
+texture = ExtResource( 2 )
+stretch_mode = 2
+
+[node name="GuideLine" type="Line2D" parent="Sprite2D"]
+modulate = Color( 1, 1, 1, 0.1 )
+show_behind_parent = true
+position = Vector2( 0, 10 )
+points = PackedVector2Array( 135, 2, 2000, 2 )
+width = 5.0
+default_color = Color( 1, 1, 1, 1 )
+texture_mode = 1
+joint_mode = 2
+sharp_limit = 2.0
+round_precision = 10
+
+[node name="CannonTip" type="Marker2D" parent="Sprite2D"]
+position = Vector2( 120, 12 )
+
+[node name="CannonBase" type="Marker2D" parent="Sprite2D"]
+position = Vector2( 0, 12 )
+
+[node name="Projectiles" type="Node" parent="."]
+
+[node name="FireCooldown" type="Timer" parent="."]
+process_mode = 1
+wait_time = 1.0
+one_shot = true
+autostart = true
+
diff --git a/test-project/scenes/ColoredEntity.tscn b/test-project/scenes/ColoredEntity.tscn
new file mode 100755
index 0000000..d254091
--- /dev/null
+++ b/test-project/scenes/ColoredEntity.tscn
@@ -0,0 +1,7 @@
+[gd_scene load_steps=2 format=2]
+
+[ext_resource path="res://scripts/ColoredEntity.gd" type="Script" id=1]
+
+[node name="ColoredEntity" type="Node2D"]
+script = ExtResource( 1 )
+
diff --git a/test-project/scenes/EnemyGenerator.tscn b/test-project/scenes/EnemyGenerator.tscn
new file mode 100644
index 0000000..f9a6230
--- /dev/null
+++ b/test-project/scenes/EnemyGenerator.tscn
@@ -0,0 +1,31 @@
+[gd_scene load_steps=3 format=3 uid="uid://b2fpxxnc2fq3o"]
+
+[ext_resource type="Script" path="res://scripts/EnemyGenerator.gd" id="1"]
+
+[sub_resource type="Curve2D" id="1"]
+_data = {
+"points": PackedVector2Array(0, 0, 0, 0, 802.371, -163.584, 0, 0, 0, 0, 959.909, -42.497, 0, 0, 0, 0, 1955.58, -35.2126, 0, 0, 0, 0, 1977.69, -34.257, 0, 0, 0, 0, 2072.89, -95.3282, 0, 0, 0, 0, 2171.96, -161.2, 0, 0, 0, 0, 1329.54, -162.398, 0, 0, 0, 0, 958.623, -161.461, 0, 0, 0, 0, 801.772, -163.584, 0, 0, 0, 0, 802.371, -163.584)
+}
+point_count = 10
+
+[node name="EnemyGenerator" type="Node2D"]
+script = ExtResource("1")
+
+[node name="Enemies" type="Node" parent="."]
+
+[node name="SpawnArea" type="Path2D" parent="."]
+self_modulate = Color(0.5, 0.6, 1, 0.7)
+curve = SubResource("1")
+
+[node name="SpawnLocation" type="PathFollow2D" parent="SpawnArea"]
+position = Vector2(802.371, -163.584)
+rotation = 0.655312
+
+[node name="SpawnTimer" type="Timer" parent="."]
+process_mode = 1
+wait_time = 1.5
+autostart = true
+
+[connection signal="start" from="." to="." method="_on_EnemyGenerator_start"]
+[connection signal="stop" from="." to="." method="_on_EnemyGenerator_stop"]
+[connection signal="timeout" from="SpawnTimer" to="." method="_on_SpawnTimer_timeout"]
diff --git a/test-project/scenes/GLOBAL.tscn b/test-project/scenes/GLOBAL.tscn
new file mode 100644
index 0000000..21604c8
--- /dev/null
+++ b/test-project/scenes/GLOBAL.tscn
@@ -0,0 +1,6 @@
+[gd_scene load_steps=2 format=3 uid="uid://cch0y3px40vrg"]
+
+[ext_resource type="Script" path="res://scripts/GLOBAL.gd" id="1"]
+
+[node name="GLOBAL" type="Node"]
+script = ExtResource("1")
diff --git a/test-project/scenes/Game.tscn b/test-project/scenes/Game.tscn
new file mode 100755
index 0000000..9649028
--- /dev/null
+++ b/test-project/scenes/Game.tscn
@@ -0,0 +1,21 @@
+[gd_scene load_steps=6 format=2]
+
+[ext_resource path="res://scripts/Game.gd" type="Script" id=1]
+[ext_resource path="res://scenes/Arena.tscn" type="PackedScene" id=2]
+[ext_resource path="res://scenes/Player.tscn" type="PackedScene" id=3]
+[ext_resource path="res://scenes/Base.tscn" type="PackedScene" id=4]
+[ext_resource path="res://scenes/EnemyGenerator.tscn" type="PackedScene" id=5]
+
+[node name="Game" type="Node2D"]
+script = ExtResource( 1 )
+
+[node name="Arena" parent="." instance=ExtResource( 2 )]
+
+[node name="Player" parent="." instance=ExtResource( 3 )]
+position = Vector2( 456.975, 971.301 )
+
+[node name="Base" parent="." instance=ExtResource( 4 )]
+position = Vector2( 94.6306, 955.095 )
+
+[node name="EnemyGenerator" parent="." instance=ExtResource( 5 )]
+
diff --git a/test-project/scenes/HUD.tscn b/test-project/scenes/HUD.tscn
new file mode 100755
index 0000000..ccdee48
--- /dev/null
+++ b/test-project/scenes/HUD.tscn
@@ -0,0 +1,128 @@
+[gd_scene load_steps=2 format=2]
+
+[ext_resource path="res://scripts/HUD.gd" type="Script" id=1]
+
+[node name="HUD" type="Control"]
+anchor_left = 0.0
+anchor_top = 0.0
+anchor_right = 1.0
+anchor_bottom = 1.0
+pivot_offset = Vector2( 0, 0 )
+clip_contents = false
+mouse_filter = 0
+mouse_default_cursor_shape = 0
+size_flags_horizontal = 1
+size_flags_vertical = 1
+script = ExtResource( 1 )
+
+[node name="ThemeButtons" type="HBoxContainer" parent="."]
+anchor_left = 0.0
+anchor_top = 0.0
+anchor_right = 0.0
+anchor_bottom = 0.0
+offset_left = 1.0
+offset_top = 84.0
+offset_right = 496.0
+offset_bottom = 196.0
+pivot_offset = Vector2( 0, 0 )
+clip_contents = false
+mouse_filter = 1
+mouse_default_cursor_shape = 0
+size_flags_horizontal = 1
+size_flags_vertical = 1
+alignment = 0
+
+[node name="Theme1" type="Button" parent="ThemeButtons"]
+anchor_left = 0.0
+anchor_top = 0.0
+anchor_right = 0.0
+anchor_bottom = 0.0
+offset_right = 63.0
+offset_bottom = 112.0
+pivot_offset = Vector2( 0, 0 )
+clip_contents = false
+focus_mode = 2
+mouse_filter = 0
+mouse_default_cursor_shape = 0
+size_flags_horizontal = 1
+size_flags_vertical = 1
+toggle_mode = false
+focus_mode = 2
+shortcut = null
+group = null
+text = "THEME1"
+flat = false
+align = 1
+
+[node name="Theme2" type="Button" parent="ThemeButtons"]
+anchor_left = 0.0
+anchor_top = 0.0
+anchor_right = 0.0
+anchor_bottom = 0.0
+offset_left = 67.0
+offset_right = 130.0
+offset_bottom = 112.0
+pivot_offset = Vector2( 0, 0 )
+clip_contents = false
+focus_mode = 2
+mouse_filter = 0
+mouse_default_cursor_shape = 0
+size_flags_horizontal = 1
+size_flags_vertical = 1
+toggle_mode = false
+focus_mode = 2
+shortcut = null
+group = null
+text = "THEME2"
+flat = false
+align = 1
+
+[node name="Theme3" type="Button" parent="ThemeButtons"]
+anchor_left = 0.0
+anchor_top = 0.0
+anchor_right = 0.0
+anchor_bottom = 0.0
+offset_left = 134.0
+offset_right = 197.0
+offset_bottom = 112.0
+pivot_offset = Vector2( 0, 0 )
+clip_contents = false
+focus_mode = 2
+mouse_filter = 0
+mouse_default_cursor_shape = 0
+size_flags_horizontal = 1
+size_flags_vertical = 1
+toggle_mode = false
+focus_mode = 2
+shortcut = null
+group = null
+text = "THEME3"
+flat = false
+align = 1
+
+[node name="StartButton" type="Button" parent="."]
+anchor_left = 0.0
+anchor_top = 0.0
+anchor_right = 0.0
+anchor_bottom = 0.0
+offset_right = 203.0
+offset_bottom = 78.0
+pivot_offset = Vector2( 0, 0 )
+clip_contents = false
+focus_mode = 2
+mouse_filter = 0
+mouse_default_cursor_shape = 0
+size_flags_horizontal = 1
+size_flags_vertical = 1
+toggle_mode = false
+focus_mode = 2
+shortcut = null
+group = null
+text = "START"
+flat = false
+align = 1
+
+[connection signal="pressed" from="ThemeButtons/Theme1" to="." method="_on_Theme_Button_pressed" binds= [ 0 ]]
+[connection signal="pressed" from="ThemeButtons/Theme2" to="." method="_on_Theme_Button_pressed" binds= [ 1 ]]
+[connection signal="pressed" from="ThemeButtons/Theme3" to="." method="_on_Theme_Button_pressed" binds= [ 2 ]]
+[connection signal="pressed" from="StartButton" to="." method="_on_StartButton_pressed"]
diff --git a/test-project/scenes/Main.tscn b/test-project/scenes/Main.tscn
new file mode 100644
index 0000000..43a69c7
--- /dev/null
+++ b/test-project/scenes/Main.tscn
@@ -0,0 +1,46 @@
+[gd_scene load_steps=5 format=3 uid="uid://bo3no7bm4rb0n"]
+
+[ext_resource type="Script" path="res://scripts/Main.gd" id="1"]
+[ext_resource type="PackedScene" path="res://scenes/Game.tscn" id="2"]
+[ext_resource type="PackedScene" path="res://scenes/HUD.tscn" id="3"]
+
+[sub_resource type="Curve2D" id="1"]
+_data = {
+"points": PackedVector2Array(0, 0, 0, 0, 18.8947, 55.4928, 0, 0, 0, 0, -269.905, -419.567)
+}
+point_count = 2
+
+[node name="Main" type="Node2D"]
+script = ExtResource("1")
+
+[node name="Game" parent="." instance=ExtResource("2")]
+
+[node name="Path2D" type="Path2D" parent="."]
+self_modulate = Color(0.5, 0.6, 1, 0.7)
+position = Vector2(460.871, 779.989)
+rotation = -3.14159
+scale = Vector2(2.52308, -0.891921)
+curve = SubResource("1")
+
+[node name="PathFollow2D" type="PathFollow2D" parent="Path2D"]
+position = Vector2(18.8947, 55.4928)
+rotation = -2.11702
+loop = false
+
+[node name="MenuCamera" type="Camera2D" parent="Path2D/PathFollow2D"]
+position = Vector2(21.2, 58.5266)
+zoom = Vector2(0.5, 0.5)
+drag_horizontal_enabled = true
+drag_vertical_enabled = true
+editor_draw_limits = true
+editor_draw_drag_margin = true
+
+[node name="HUD" parent="." instance=ExtResource("3")]
+layout_mode = 3
+anchors_preset = 15
+offset_left = 625.0
+offset_top = 820.0
+offset_right = 625.0
+offset_bottom = 820.0
+
+[connection signal="start_zoom_out" from="." to="." method="_on_Main_start_zoom_out"]
diff --git a/test-project/scenes/Player.tscn b/test-project/scenes/Player.tscn
new file mode 100755
index 0000000..b9c7899
--- /dev/null
+++ b/test-project/scenes/Player.tscn
@@ -0,0 +1,36 @@
+[gd_scene load_steps=5 format=2]
+
+[ext_resource path="res://scripts/Player.gd" type="Script" id=1]
+[ext_resource path="res://scenes/Cannon.tscn" type="PackedScene" id=2]
+[ext_resource path="res://resources/img/cat0.png" type="Texture2D" id=3]
+
+[sub_resource type="CapsuleShape2D" id=1]
+
+custom_solver_bias = 0.0
+radius = 79.0755
+height = 89.5214
+
+[node name="Player" type="Node2D" groups=[
+"ColoredEntity",
+]]
+script = ExtResource( 1 )
+
+[node name="Cannon" parent="." instance=ExtResource( 2 )]
+position = Vector2( -50.0759, 40.2842 )
+
+[node name="Sprite2D" type="Sprite2D" parent="."]
+scale = Vector2( 0.5, 0.5 )
+texture = ExtResource( 3 )
+
+[node name="Body" type="CharacterBody2D" parent="."]
+input_pickable = false
+collision_layer = 1
+collision_mask = 1
+collision/safe_margin = 0.08
+motion/sync_to_physics = false
+
+[node name="Collision" type="CollisionShape2D" parent="Body"]
+position = Vector2( -6.08298, -9.86302 )
+shape = SubResource( 1 )
+
+[connection signal="unblock" from="." to="." method="_on_Player_unblock"]
diff --git a/test-project/scenes/Projectile.tscn b/test-project/scenes/Projectile.tscn
new file mode 100644
index 0000000..3708d2b
--- /dev/null
+++ b/test-project/scenes/Projectile.tscn
@@ -0,0 +1,47 @@
+[gd_scene load_steps=7 format=3 uid="uid://dwr8aulhrbt85"]
+
+[ext_resource type="Script" path="res://scripts/Projectile.gd" id="1"]
+[ext_resource type="Script" path="res://scripts/ColoredEntity.gd" id="2"]
+[ext_resource type="Texture2D" uid="uid://my3jd38hyc7k" path="res://resources/img/missile-placeholder.png" id="3"]
+
+[sub_resource type="PhysicsMaterial" id="PhysicsMaterial_13bxk"]
+
+[sub_resource type="RectangleShape2D" id="1"]
+size = Vector2(48, 48)
+
+[sub_resource type="CircleShape2D" id="2"]
+radius = 143.093
+
+[node name="Projectile" type="RigidBody2D"]
+collision_layer = 0
+collision_mask = 0
+mass = 3.0
+physics_material_override = SubResource("PhysicsMaterial_13bxk")
+linear_damp = -1.0
+angular_damp = 100.0
+script = ExtResource("1")
+
+[node name="Mask" type="Node2D" parent="."]
+script = ExtResource("2")
+
+[node name="Sprite2D" type="Sprite2D" parent="Mask"]
+texture = ExtResource("3")
+
+[node name="Collision" type="CollisionShape2D" parent="."]
+shape = SubResource("1")
+
+[node name="VisibleOnScreenNotifier3D" type="VisibleOnScreenNotifier2D" parent="."]
+rect = Rect2(-12, -12, 22, 24)
+
+[node name="ExplosionArea" type="Area2D" parent="."]
+collision_layer = 0
+collision_mask = 0
+gravity = 98.0
+
+[node name="ExplosionShape" type="CollisionShape2D" parent="ExplosionArea"]
+shape = SubResource("2")
+
+[connection signal="body_entered" from="." to="." method="_on_Projectile_body_entered"]
+[connection signal="screen_exited" from="VisibleOnScreenNotifier3D" to="." method="_on_VisibilityNotifier2D_screen_exited"]
+[connection signal="body_entered" from="ExplosionArea" to="." method="_on_ExplosionArea_body_entered"]
+[connection signal="body_exited" from="ExplosionArea" to="." method="_on_ExplosionArea_body_exited"]
diff --git a/test-project/scripts/Arena.gd b/test-project/scripts/Arena.gd
new file mode 100755
index 0000000..644a968
--- /dev/null
+++ b/test-project/scripts/Arena.gd
@@ -0,0 +1,7 @@
+extends Node2D
+
+func _ready():
+ self.setup_arena()
+
+func setup_arena() -> void:
+ $Background.lowlight()
diff --git a/test-project/scripts/Base.gd b/test-project/scripts/Base.gd
new file mode 100755
index 0000000..30cc98c
--- /dev/null
+++ b/test-project/scripts/Base.gd
@@ -0,0 +1,22 @@
+extends "res://scripts/ColoredEntity.gd"
+
+const FULL_HEALTH: int = 3
+var health: int = self.FULL_HEALTH
+
+func _ready():
+ self.highlight()
+
+func hit() -> void:
+ if health > 1:
+ self.health -= 1
+ else:
+ self.dead()
+
+func dead() -> void:
+ pass
+
+# --- Signals ---
+
+func _on_Body_body_entered(body: Node):
+ body.queue_free()
+ self.hit()
diff --git a/test-project/scripts/Cannon.gd b/test-project/scripts/Cannon.gd
new file mode 100644
index 0000000..5a31fc9
--- /dev/null
+++ b/test-project/scripts/Cannon.gd
@@ -0,0 +1,48 @@
+extends "res://scripts/ColoredEntity.gd"
+
+@onready var Bullet: PackedScene = preload("res://scenes/Projectile.tscn")
+const RATE_OF_CHANGE: float = 0.9
+const UPPER_LIMIT: int = -89
+const LOWER_LIMIT: int = -5
+
+var angle: float = -45.0
+
+func _ready():
+ self.highlight()
+
+func _process(delta):
+ $Sprite2D.set_rotation(deg_to_rad(self.angle))
+ $Sprite2D.size.y = 24
+
+func _input(event):
+ if Input.is_action_pressed("ui_up"):
+ if self.angle > UPPER_LIMIT:
+ self.move_up()
+ if Input.is_action_pressed("ui_down"):
+ if self.angle < LOWER_LIMIT:
+ self.move_down()
+
+func move_up() -> void:
+ self.angle -= RATE_OF_CHANGE
+
+func move_down() -> void:
+ self.angle += RATE_OF_CHANGE
+
+func shoot() -> void:
+ if $FireCooldown.time_left == 0:
+ var NewBullet = Bullet.instantiate()
+ NewBullet.global_position = $Sprite2D/CannonTip.global_position
+ NewBullet.rotation_degrees = self.angle
+ var at: Vector2 = $Sprite2D/CannonTip.global_position - $Sprite2D/CannonBase.global_position
+ NewBullet.shoot(at)
+
+ var BulletSprite = NewBullet.get_node("Mask")
+ if self.highlighted:
+ BulletSprite.highlight()
+ else:
+ BulletSprite.lowlight()
+
+ NewBullet.setup(GLOBAL.SpriteType.BULLET, GLOBAL.BULLET_SPEED)
+ NewBullet.update_collision_layer()
+ $Projectiles.add_child(NewBullet)
+ $FireCooldown.start()
diff --git a/test-project/scripts/ColoredEntity.gd b/test-project/scripts/ColoredEntity.gd
new file mode 100755
index 0000000..bf498e3
--- /dev/null
+++ b/test-project/scripts/ColoredEntity.gd
@@ -0,0 +1,37 @@
+# Class with common color switch operations.
+# Add to the group ColoredEntity if its color is not fixed and gets swapped with player input.
+extends Node2D
+
+var current_color: Color = GLOBAL.LOWLIGHT
+var highlighted: bool = false
+
+func _ready():
+ self.update_color()
+
+func update_color() -> void:
+ if highlighted:
+ self.current_color = GLOBAL.HIGHLIGHT
+ else:
+ self.current_color = GLOBAL.LOWLIGHT
+ self.modulate = self.current_color
+
+func swap_color() -> void:
+ if current_color == GLOBAL.LOWLIGHT:
+ self.highlight()
+ else:
+ self.lowlight()
+
+func highlight():
+ current_color = GLOBAL.HIGHLIGHT
+ highlighted = true
+ self.update_color()
+
+func lowlight():
+ current_color = GLOBAL.LOWLIGHT
+ highlighted = false
+ self.update_color()
+
+func set_random_color() -> void:
+ randomize()
+ if randi() % 2 == 0:
+ self.swap_color()
diff --git a/test-project/scripts/EnemyGenerator.gd b/test-project/scripts/EnemyGenerator.gd
new file mode 100644
index 0000000..99d7c44
--- /dev/null
+++ b/test-project/scripts/EnemyGenerator.gd
@@ -0,0 +1,40 @@
+extends Node2D
+
+@onready var EnemyScene: PackedScene = preload("res://scenes/Projectile.tscn")
+signal start
+signal stop
+
+var Enemy
+
+func _ready():
+ self.setup_enemy()
+ self.emit_signal("stop")
+
+func setup_enemy() -> void:
+ self.Enemy = EnemyScene.instantiate()
+ Enemy.setup(GLOBAL.SpriteType.MISSILE, GLOBAL.MISSILE_SPEED["max"])
+
+func spawn_and_shoot_enemy() -> void:
+ var Duplicate = Enemy.duplicate(Node.DUPLICATE_USE_INSTANTIATION)
+ Duplicate.set_random_color()
+ Duplicate.update_collision_layer()
+ $Enemies.add_child(Duplicate)
+
+ $SpawnArea/SpawnLocation.set_h_offset(randi())
+ Duplicate.global_position = $SpawnArea/SpawnLocation.position
+
+ var direction: Vector2 = (Vector2(0, 1080) - Duplicate.global_position).normalized()
+ var angle: float = Vector2(1, 0).angle_to(direction)
+ Duplicate.rotation = angle
+ Duplicate.shoot_missile(direction)
+
+# --- Signals ---
+
+func _on_SpawnTimer_timeout():
+ self.spawn_and_shoot_enemy()
+
+func _on_EnemyGenerator_start():
+ $SpawnTimer.start()
+
+func _on_EnemyGenerator_stop():
+ $SpawnTimer.stop()
diff --git a/test-project/scripts/GLOBAL.gd b/test-project/scripts/GLOBAL.gd
new file mode 100755
index 0000000..922d183
--- /dev/null
+++ b/test-project/scripts/GLOBAL.gd
@@ -0,0 +1,44 @@
+extends Node
+
+enum SpriteType { BULLET = 0, MISSILE = 1 }
+
+var theme_index: int = 0
+var current_theme: Dictionary
+var HIGHLIGHT: Color
+var LOWLIGHT: Color
+
+const COLORSET_PY: Dictionary = { "high": Color(1, 1, 0.25), "low": Color(0.50, 0, 1) }
+const COLORSET_OB: Dictionary = { "high": Color(1, 0.56, 0), "low": Color(0, 0.40, 1) }
+const COLORSET_PB: Dictionary = { "high": Color(1, 0.50, 1), "low": Color(0, 0.75, 1) }
+const COLORS: Array = [
+ COLORSET_PY,
+ COLORSET_OB,
+ COLORSET_PB
+]
+
+const DEF_SPEED: float = 5.0
+const MISSILE_SPEED: Dictionary = { "min": 30, "max": 60 }
+const BULLET_SPEED: float = 10.0
+
+const LOW_COLLISION: int = 2
+const HIGH_COLLISION: int = 3
+
+func _ready():
+ self.update_global_theme(self.theme_index)
+
+func update_global_theme(index: int):
+ self.theme_index = index
+ self.current_theme = COLORS[theme_index]
+ self.HIGHLIGHT = self.current_theme["high"]
+ self.LOWLIGHT = self.current_theme["low"]
+ self.update_colored_entities()
+
+func update_colored_entities() -> void:
+ var nodes: Array = self.get_tree().get_nodes_in_group("ColoredEntity")
+ for node in nodes:
+ node.update_color()
+
+func swap_nodes_color() -> void:
+ var nodes: Array = self.get_tree().get_nodes_in_group("ColoredEntity")
+ for node in nodes:
+ node.swap_color()
diff --git a/test-project/scripts/Game.gd b/test-project/scripts/Game.gd
new file mode 100755
index 0000000..09ce089
--- /dev/null
+++ b/test-project/scripts/Game.gd
@@ -0,0 +1,4 @@
+extends Node2D
+
+func _ready():
+ pass
diff --git a/test-project/scripts/HUD.gd b/test-project/scripts/HUD.gd
new file mode 100755
index 0000000..53e7b43
--- /dev/null
+++ b/test-project/scripts/HUD.gd
@@ -0,0 +1,15 @@
+extends Control
+
+func _ready():
+ pass
+
+# --- Signals ---
+
+func _on_Theme_Button_pressed(button_index: int) -> void:
+ GLOBAL.update_global_theme(button_index)
+
+func _on_StartButton_pressed():
+ get_node("/root/Main").emit_signal("start_zoom_out")
+ get_node("/root/Main/Game/EnemyGenerator").emit_signal("start")
+ get_node("/root/Main/Game/Player").emit_signal("unblock")
+ self.hide()
diff --git a/test-project/scripts/Main.gd b/test-project/scripts/Main.gd
new file mode 100755
index 0000000..9ce66fc
--- /dev/null
+++ b/test-project/scripts/Main.gd
@@ -0,0 +1,25 @@
+extends Node2D
+
+signal start_zoom_out
+
+const ZOOM_DELTA: float = 0.2
+const MOVE_DELTA: float = 0.353
+
+@onready var camera: Camera2D = $Path2D/PathFollow2D/MenuCamera
+var camera_zooming: bool = false
+
+func _process(delta: float):
+ if camera_zooming:
+ self.zoom_out_proccess(delta)
+
+func zoom_out_proccess(delta: float) -> void:
+ var delta_speed = delta * ZOOM_DELTA
+ if camera.zoom < Vector2(1, 1):
+ camera.zoom += Vector2(delta_speed, delta_speed)
+ else:
+ self.camera_zooming = false
+
+ $Path2D/PathFollow2D.progress_ratio += delta * MOVE_DELTA
+
+func _on_Main_start_zoom_out():
+ self.camera_zooming = true
diff --git a/test-project/scripts/Player.gd b/test-project/scripts/Player.gd
new file mode 100755
index 0000000..8ce8a6a
--- /dev/null
+++ b/test-project/scripts/Player.gd
@@ -0,0 +1,25 @@
+extends "res://scripts/ColoredEntity.gd"
+
+signal unblock
+
+var blocked_controls: bool = true
+
+func _ready():
+ self.highlight()
+
+func _input(event):
+ if blocked_controls:
+ return
+
+ if Input.is_action_just_pressed("swap"):
+ GLOBAL.swap_nodes_color()
+ if Input.is_action_pressed("fire"):
+ self.shoot_bullet()
+
+func shoot_bullet() -> void:
+ $Cannon.shoot()
+
+# --- Signals ---
+
+func _on_Player_unblock():
+ self.blocked_controls = false
diff --git a/test-project/scripts/Projectile.gd b/test-project/scripts/Projectile.gd
new file mode 100644
index 0000000..b7344d1
--- /dev/null
+++ b/test-project/scripts/Projectile.gd
@@ -0,0 +1,68 @@
+extends RigidBody2D
+
+var MissileSprite: Texture2D = load("res://resources/img/missile-placeholder.png")
+var BulletSprite: Texture2D = load("res://resources/img/bullet-placeholder.png")
+
+var bodies_in_area: Array = []
+var sprite_type: int
+var speed: float = GLOBAL.DEF_SPEED
+var armed: bool = false
+
+func _ready():
+ self.gravity_scale = 0.0
+ self.physics_material_override.friction = 0.0
+ self.contact_monitor = true
+ self.max_contacts_reported = 1
+
+func setup(sprite_type: int, speed: float) -> void:
+ self.sprite_type = sprite_type
+ self.speed = speed
+
+ if sprite_type == GLOBAL.SpriteType.BULLET:
+ $Mask/Sprite2D.set_texture(self.BulletSprite)
+ self.armed = true
+ $ExplosionArea/ExplosionShape.disabled = false
+ else:
+ $Mask/Sprite2D.set_texture(self.MissileSprite)
+
+func shoot(at: Vector2) -> void:
+ var direction = (at - self.global_position)
+ self.linear_velocity = at * speed
+
+func shoot_missile(at: Vector2) -> void:
+ var direction = (at - self.global_position)
+ self.apply_central_force(at * GLOBAL.MISSILE_SPEED["max"])
+
+func set_random_color() -> void:
+ $Mask.set_random_color()
+
+func update_collision_layer() -> void:
+ if $Mask.highlighted:
+ self.set_collision_layer_value(GLOBAL.HIGH_COLLISION, 1)
+ self.set_collision_mask_value(GLOBAL.HIGH_COLLISION, 1)
+ $ExplosionArea.set_collision_layer_value(GLOBAL.HIGH_COLLISION, 1)
+ $ExplosionArea.set_collision_mask_value(GLOBAL.HIGH_COLLISION, 1)
+ else:
+ self.set_collision_layer_value(GLOBAL.LOW_COLLISION, 1)
+ self.set_collision_mask_value(GLOBAL.LOW_COLLISION, 1)
+ $ExplosionArea.set_collision_layer_value(GLOBAL.LOW_COLLISION, 1)
+ $ExplosionArea.set_collision_mask_value(GLOBAL.LOW_COLLISION, 1)
+
+# --- Signals ---
+
+func _on_VisibilityNotifier2D_screen_exited():
+ self.queue_free()
+
+func _on_Projectile_body_entered(body: PhysicsBody2D):
+ if armed:
+ for missile in bodies_in_area:
+ missile.queue_free()
+ self.queue_free()
+
+func _on_ExplosionArea_body_entered(body: PhysicsBody2D):
+ if armed and body.get_node("Mask").highlighted == $Mask.highlighted:
+ bodies_in_area.push_front(body)
+
+func _on_ExplosionArea_body_exited(body: PhysicsBody2D):
+ if armed:
+ bodies_in_area.erase(body)