Basic game template addon
This commit is contained in:
70
addons/maaacks_game_template/extras/scripts/asset_checker.sh
Normal file
70
addons/maaacks_game_template/extras/scripts/asset_checker.sh
Normal file
@@ -0,0 +1,70 @@
|
||||
#!/bin/bash
|
||||
# asset checker command
|
||||
# Used for quickly checking that assets (like audio files) are being used where expected.
|
||||
#
|
||||
# Recursively searches through scene files (.tscn, .scn, .res)
|
||||
# for occurrences of asset types (default: AudioStream).
|
||||
# It then outputs the paths of assets discovered,
|
||||
# along with the file names that use them.
|
||||
|
||||
short_flag=false
|
||||
asset_type="AudioStream"
|
||||
|
||||
print_usage() {
|
||||
printf "Usage: -sa %s\n" "$asset_type"
|
||||
}
|
||||
|
||||
while getopts 'a:s' flag; do
|
||||
case "${flag}" in
|
||||
a)
|
||||
asset_type="${OPTARG}"
|
||||
;;
|
||||
s)
|
||||
short_flag=true
|
||||
;;
|
||||
*)
|
||||
print_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Initialize an associative array to store paths and corresponding files
|
||||
declare -A path_files
|
||||
|
||||
while IFS=: read -r file line; do
|
||||
path=$(echo "$line" | grep -o 'path="[^"]*' | cut -d'"' -f2)
|
||||
if [ -n "$path" ]; then
|
||||
# Append the current file to the string of files for this path
|
||||
# Note: Bash does not support having arrays as values of associative array.
|
||||
# Using a pipe `|` separator instead, and then splitting on output
|
||||
if [ -z "${path_files["$path"]}" ]; then
|
||||
path_files["$path"]=$file
|
||||
else
|
||||
path_files["$path"]+="|$file"
|
||||
fi
|
||||
fi
|
||||
done < <(egrep -ir --include=*.{tscn,scn,res} "type=\"$asset_type\"")
|
||||
|
||||
# Get the paths and sort them
|
||||
sorted_paths=()
|
||||
for key in "${!path_files[@]}"; do
|
||||
sorted_paths+=("$key")
|
||||
done
|
||||
IFS=$'\n' sorted_paths=($(sort <<< "${sorted_paths[*]}"))
|
||||
unset IFS
|
||||
|
||||
# Print out the results
|
||||
for path in "${sorted_paths[@]}"; do
|
||||
# Note: Bash does not support having arrays as values of associative array.
|
||||
# Splitting the concatenated files string on the pipe `|` separator.
|
||||
IFS='|' read -r -a files_array <<< "${path_files[$path]}"
|
||||
files_count=${#files_array[@]}
|
||||
printf "%-80s | Uses: %s\n" "$path" "$files_count"
|
||||
if ! $short_flag ; then
|
||||
for file in "${files_array[@]}"; do
|
||||
printf "\t%82s\n" "$file"
|
||||
done
|
||||
echo
|
||||
fi
|
||||
done
|
||||
@@ -0,0 +1,281 @@
|
||||
# MIT License
|
||||
|
||||
# Copyright (c) 2018 BARICHELLO
|
||||
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
# Original file: https://github.com/abarichello/godot-ci/blob/master/.github/workflows/godot-ci.yml
|
||||
# This edited version is triggered using a Github Release and uploads artifacts to the release.
|
||||
# Furthermore, a new job (upload to itch.io using butler) has been added.
|
||||
# Modified by: Nicolas Oulianov (github @oulianov) & Marek Belski
|
||||
|
||||
name: "Build and Deploy to Itch.io"
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
# Allow release asset uploads
|
||||
contents: write
|
||||
|
||||
env:
|
||||
# Set this repository variable to the version to build with (>=4)
|
||||
# This will be used for all container images: barichello/godot-ci:[#.#]
|
||||
# Look at available versions here: https://hub.docker.com/r/barichello/godot-ci/tags
|
||||
GODOT_VERSION: ${{ vars.GODOT_VERSION }}
|
||||
# Set this repository variable to your game name (it will be the filename downloaded)
|
||||
EXPORT_NAME: ${{ vars.EXPORT_NAME }}
|
||||
# NOTE: If your `project.godot` is at the repository root, set `PROJECT_PATH` to "."
|
||||
# If it's in a subdirectory, set it to the subdirectory name (e.g., "your-game")
|
||||
PROJECT_PATH: .
|
||||
|
||||
jobs:
|
||||
export-web:
|
||||
name: Web Export
|
||||
runs-on: ubuntu-24.04
|
||||
container:
|
||||
image: barichello/godot-ci:${{ vars.GODOT_VERSION }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
lfs: true
|
||||
|
||||
- name: Setup
|
||||
run: |
|
||||
mkdir -v -p ~/.local/share/godot/export_templates/
|
||||
mkdir -v -p ~/.config/
|
||||
mv /root/.config/godot ~/.config/godot || true
|
||||
mv /root/.local/share/godot/export_templates/${GODOT_VERSION}.stable ~/.local/share/godot/export_templates/${GODOT_VERSION}.stable || true
|
||||
|
||||
- name: Web Build
|
||||
run: |
|
||||
mkdir -v -p build/web
|
||||
EXPORT_DIR="$(readlink -f build)"
|
||||
cd $PROJECT_PATH
|
||||
godot --headless --verbose --export-release "Web" "$EXPORT_DIR/web/index.html"
|
||||
|
||||
- name: Prepare web release asset (zip)
|
||||
run: |
|
||||
# ensure zip is available in the container
|
||||
if ! command -v zip >/dev/null 2>&1; then
|
||||
apt-get update && apt-get install -y zip
|
||||
fi
|
||||
# Change to the web directory and zip its contents directly
|
||||
cd build/web
|
||||
zip -r ../${EXPORT_NAME}-web.zip .
|
||||
ls -lh ../${EXPORT_NAME}-web.zip
|
||||
shell: bash
|
||||
|
||||
- name: Upload to GitHub Release (if this run is a release)
|
||||
if: ${{ github.event_name == 'release' }}
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
file: build/${{ env.EXPORT_NAME }}-web.zip
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: web
|
||||
path: build/web
|
||||
|
||||
export-windows:
|
||||
name: Windows Export
|
||||
runs-on: ubuntu-24.04
|
||||
container:
|
||||
image: barichello/godot-ci:${{ vars.GODOT_VERSION }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
lfs: true
|
||||
|
||||
- name: Setup
|
||||
run: |
|
||||
mkdir -v -p ~/.local/share/godot/export_templates/
|
||||
mkdir -v -p ~/.config/
|
||||
mv /root/.config/godot ~/.config/godot || true
|
||||
mv /root/.local/share/godot/export_templates/${GODOT_VERSION}.stable ~/.local/share/godot/export_templates/${GODOT_VERSION}.stable || true
|
||||
|
||||
- name: Windows Build
|
||||
run: |
|
||||
mkdir -v -p build/windows
|
||||
EXPORT_DIR="$(readlink -f build)"
|
||||
cd $PROJECT_PATH
|
||||
godot --headless --verbose --export-release "Windows Desktop" "$EXPORT_DIR/windows/${EXPORT_NAME}.exe"
|
||||
|
||||
- name: Archive Build
|
||||
run: |
|
||||
# ensure zip is available in the container
|
||||
if ! command -v zip >/dev/null 2>&1; then
|
||||
apt-get update && apt-get install -y zip
|
||||
fi
|
||||
# Change to the web directory and zip its contents directly
|
||||
cd build/windows
|
||||
zip -r ../${EXPORT_NAME}-windows.zip .
|
||||
ls -lh ../${EXPORT_NAME}-windows.zip
|
||||
shell: bash
|
||||
|
||||
- name: Upload to GitHub Release (if this run is a release)
|
||||
if: ${{ github.event_name == 'release' }}
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
file: build/${{ env.EXPORT_NAME }}-windows.zip
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows
|
||||
path: build/${{ env.EXPORT_NAME }}-windows.zip
|
||||
|
||||
export-linux:
|
||||
name: Linux Export
|
||||
runs-on: ubuntu-24.04
|
||||
container:
|
||||
image: barichello/godot-ci:${{ vars.GODOT_VERSION }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
lfs: true
|
||||
|
||||
- name: Setup
|
||||
run: |
|
||||
mkdir -v -p ~/.local/share/godot/export_templates/
|
||||
mkdir -v -p ~/.config/
|
||||
mv /root/.config/godot ~/.config/godot || true
|
||||
mv /root/.local/share/godot/export_templates/${GODOT_VERSION}.stable ~/.local/share/godot/export_templates/${GODOT_VERSION}.stable || true
|
||||
|
||||
- name: Linux Build
|
||||
run: |
|
||||
mkdir -v -p build/linux
|
||||
EXPORT_DIR="$(readlink -f build)"
|
||||
cd $PROJECT_PATH
|
||||
godot --headless --verbose --export-release "Linux" "$EXPORT_DIR/linux/${EXPORT_NAME}.x86_64"
|
||||
|
||||
- name: Archive Build
|
||||
run: |
|
||||
# ensure zip is available in the container
|
||||
if ! command -v zip >/dev/null 2>&1; then
|
||||
apt-get update && apt-get install -y zip
|
||||
fi
|
||||
# Change to the web directory and zip its contents directly
|
||||
cd build/linux
|
||||
zip -r ../${EXPORT_NAME}-linux.zip .
|
||||
ls -lh ../${EXPORT_NAME}-linux.zip
|
||||
shell: bash
|
||||
|
||||
- name: Upload to GitHub Release (if this run is a release)
|
||||
if: ${{ github.event_name == 'release' }}
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
file: build/${{ env.EXPORT_NAME }}-linux.zip
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: linux
|
||||
path: build/${{ env.EXPORT_NAME }}-linux.zip
|
||||
|
||||
export-mac:
|
||||
name: macOS Export
|
||||
runs-on: ubuntu-24.04
|
||||
container:
|
||||
image: barichello/godot-ci:${{ vars.GODOT_VERSION }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
lfs: true
|
||||
|
||||
- name: Setup
|
||||
run: |
|
||||
mkdir -v -p ~/.local/share/godot/export_templates/
|
||||
mkdir -v -p ~/.config/
|
||||
mv /root/.config/godot ~/.config/godot || true
|
||||
mv /root/.local/share/godot/export_templates/${GODOT_VERSION}.stable ~/.local/share/godot/export_templates/${GODOT_VERSION}.stable || true
|
||||
|
||||
- name: Mac Build
|
||||
run: |
|
||||
mkdir -v -p build/mac
|
||||
EXPORT_DIR="$(readlink -f build)"
|
||||
cd $PROJECT_PATH
|
||||
godot --headless --verbose --export-release "macOS" "$EXPORT_DIR/mac/${EXPORT_NAME}-mac.zip"
|
||||
|
||||
- name: Upload to GitHub Release (if this run is a release)
|
||||
if: ${{ github.event_name == 'release' }}
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
file: build/mac/${{ env.EXPORT_NAME }}-mac.zip
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: mac
|
||||
path: build/mac
|
||||
|
||||
deploy-to-itch:
|
||||
name: Deploy to Itch.io
|
||||
needs: [export-web, export-windows, export-linux, export-mac]
|
||||
runs-on: ubuntu-24.04
|
||||
env:
|
||||
BUTLER_API_KEY: ${{ secrets.BUTLER_API_KEY }} # Create this SECRET on Github
|
||||
ITCH_USERNAME: ${{ vars.ITCH_USERNAME }} # Create this VARIABLE on Github
|
||||
ITCH_GAME: ${{ vars.ITCH_GAME }} # Create this VARIABLE on Github
|
||||
steps:
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: builds
|
||||
|
||||
- name: Install Butler
|
||||
run: |
|
||||
curl -L -o butler.zip https://broth.itch.zone/butler/linux-amd64/LATEST/archive/default
|
||||
unzip butler.zip
|
||||
chmod +x butler
|
||||
./butler -V
|
||||
|
||||
- name: Get version from tag or set default
|
||||
id: version
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "release" ]; then
|
||||
VERSION="${{ github.event.release.tag_name }}"
|
||||
else
|
||||
VERSION="dev-${{ github.run_number }}"
|
||||
fi
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "Deploying version: $VERSION"
|
||||
|
||||
- name: Upload to Itch.io - Web
|
||||
run: |
|
||||
./butler push builds/web ${{ env.ITCH_USERNAME }}/${{ env.ITCH_GAME }}:html5 --userversion "${{ steps.version.outputs.version }}"
|
||||
|
||||
- name: Upload to Itch.io - Windows
|
||||
run: |
|
||||
./butler push builds/windows ${{ env.ITCH_USERNAME }}/${{ env.ITCH_GAME }}:windows --userversion "${{ steps.version.outputs.version }}"
|
||||
|
||||
- name: Upload to Itch.io - Linux
|
||||
run: |
|
||||
./butler push builds/linux ${{ env.ITCH_USERNAME }}/${{ env.ITCH_GAME }}:linux --userversion "${{ steps.version.outputs.version }}"
|
||||
|
||||
- name: Upload to Itch.io - macOS
|
||||
run: |
|
||||
./butler push builds/mac ${{ env.ITCH_USERNAME }}/${{ env.ITCH_GAME }}:mac --userversion "${{ steps.version.outputs.version }}"
|
||||
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
# butler manager command
|
||||
# Uploads directories as builds to matching itch.io channels.
|
||||
# HTML5 => html5
|
||||
# Linux => linux
|
||||
# Windows => win
|
||||
# MacOS => osx
|
||||
|
||||
file=upload_destination.txt
|
||||
directories=("HTML5" "Linux" "Windows" "MacOS")
|
||||
channels=("html5" "linux" "win" "osx")
|
||||
|
||||
# Check if the file exists
|
||||
if [ ! -e $file ]; then
|
||||
# File doesn't exist, create an empty one
|
||||
touch $file
|
||||
fi
|
||||
|
||||
# File exists, read the first line into a variable
|
||||
read -r destination < $file
|
||||
|
||||
if [ -z "$destination" ]; then
|
||||
# File is empty, prompt the user for input
|
||||
echo "Please enter the build destination (username/project-url-after-slash)."
|
||||
read -r user_input
|
||||
|
||||
# Save user input to the file
|
||||
echo "$user_input" > "$file"
|
||||
echo "Destination saved to $file."
|
||||
destination="$user_input"
|
||||
fi
|
||||
|
||||
# Check for the existence of directories and upload contents
|
||||
for ((i=0; i<${#directories[@]}; i++)); do
|
||||
dir="${directories[i]}"
|
||||
channel="${channels[i]}"
|
||||
|
||||
if [ -d "$dir" ]; then
|
||||
echo butler push ./$dir/ $destination:$channel
|
||||
butler push ./$dir/ $destination:$channel
|
||||
else
|
||||
echo "Directory '$dir' does not exist."
|
||||
fi
|
||||
done
|
||||
@@ -0,0 +1,8 @@
|
||||
extends Control
|
||||
## Control node that captures the mouse for games that require it.
|
||||
##
|
||||
## Used for games that use the mouse to move the camera (ex. FPS or third-person shooters).
|
||||
|
||||
func _gui_input(event):
|
||||
if event is InputEventMouseButton and Input.mouse_mode != Input.MOUSE_MODE_CAPTURED:
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
|
||||
@@ -0,0 +1 @@
|
||||
uid://dqdyrkm3jily6
|
||||
51
addons/maaacks_game_template/extras/scripts/level_loader.gd
Normal file
51
addons/maaacks_game_template/extras/scripts/level_loader.gd
Normal file
@@ -0,0 +1,51 @@
|
||||
@tool
|
||||
class_name LevelLoader
|
||||
extends Node
|
||||
## Loads scenes into a container.
|
||||
|
||||
signal level_load_started
|
||||
signal level_loaded
|
||||
signal level_ready
|
||||
|
||||
## Container where the level instance will be added.
|
||||
@export var level_container : Node
|
||||
## Optional reference to a loading screen in the scene.
|
||||
## Requires Maaack's Scene Loader.
|
||||
@export var level_loading_screen : Node
|
||||
@export_group("Debugging")
|
||||
@export var current_level : Node
|
||||
|
||||
## If Maaack's Scene Loader is installed, then it will be used to change scenes.
|
||||
@onready var scene_loader_node = get_tree().root.get_node_or_null(^"SceneLoader")
|
||||
|
||||
var is_loading : bool = false
|
||||
|
||||
func _attach_level(level_resource : Resource):
|
||||
assert(level_container != null, "level_container is null")
|
||||
var instance = level_resource.instantiate()
|
||||
level_container.call_deferred("add_child", instance)
|
||||
return instance
|
||||
|
||||
func load_level(level_path : String):
|
||||
if is_loading : return
|
||||
if is_instance_valid(current_level):
|
||||
current_level.queue_free()
|
||||
await current_level.tree_exited
|
||||
current_level = null
|
||||
if scene_loader_node:
|
||||
is_loading = true
|
||||
scene_loader_node.load_scene(level_path, true)
|
||||
if level_loading_screen:
|
||||
level_loading_screen.reset()
|
||||
level_load_started.emit()
|
||||
await scene_loader_node.scene_loaded
|
||||
is_loading = false
|
||||
current_level = _attach_level(scene_loader_node.get_resource())
|
||||
if level_loading_screen:
|
||||
level_loading_screen.close()
|
||||
else:
|
||||
var level_scene = load(level_path)
|
||||
current_level = _attach_level(level_scene)
|
||||
level_loaded.emit()
|
||||
await current_level.ready
|
||||
level_ready.emit()
|
||||
@@ -0,0 +1 @@
|
||||
uid://bbymrin0cm704
|
||||
183
addons/maaacks_game_template/extras/scripts/level_manager.gd
Normal file
183
addons/maaacks_game_template/extras/scripts/level_manager.gd
Normal file
@@ -0,0 +1,183 @@
|
||||
class_name LevelManager
|
||||
extends Node
|
||||
## Manage level changes in games.
|
||||
##
|
||||
## A helper script to assign to a node in a scene.
|
||||
## It works with a level loader and can open menus when players win or lose.
|
||||
## It can either be assigned a starting level path or a scene lister.
|
||||
## It can detect signals from levels to change levels in an open-world.
|
||||
## With a scene lister, it will instead traverse through levels linearly.
|
||||
|
||||
## Required reference to a level loader in the scene.
|
||||
@export var level_loader : LevelLoader
|
||||
## Optional path to a starting level scene.
|
||||
## Required if there is no scene lister.
|
||||
@export_file var starting_level_path : String
|
||||
## Optional reference to a scene lister in the scene.
|
||||
## Required if there is no starting level path.
|
||||
@export var scene_lister : SceneLister
|
||||
## Whether to load the starting level when ready.
|
||||
@export var auto_load : bool = true
|
||||
@export_group("Scenes")
|
||||
## Path to a main menu scene.
|
||||
@export_file("*.tscn") var main_menu_scene_path : String
|
||||
## Optional path to an ending scene.
|
||||
@export_file("*.tscn") var ending_scene_path : String
|
||||
## Optional screen to be shown after the game is won.
|
||||
@export var game_won_scene : PackedScene
|
||||
## Optional screen to be shown after the level is lost.
|
||||
@export var level_lost_scene : PackedScene
|
||||
## Optional screen to be shown after the level is won.
|
||||
@export var level_won_scene : PackedScene
|
||||
|
||||
## If Maaack's Scene Loader is installed, then it will be used to change scenes.
|
||||
@onready var scene_loader_node = get_tree().root.get_node_or_null(^"SceneLoader")
|
||||
|
||||
## Reference to the current level node.
|
||||
var current_level : Node
|
||||
var current_level_path : String : set = set_current_level_path
|
||||
var checkpoint_level_path : String : set = set_checkpoint_level_path
|
||||
|
||||
func set_current_level_path(value : String) -> void:
|
||||
current_level_path = value
|
||||
|
||||
func set_checkpoint_level_path(value : String) -> void:
|
||||
checkpoint_level_path = value
|
||||
|
||||
func _try_connecting_signal_to_node(node : Node, signal_name : String, callable : Callable) -> void:
|
||||
if node.has_signal(signal_name) and not node.is_connected(signal_name, callable):
|
||||
node.connect(signal_name, callable)
|
||||
|
||||
func _try_connecting_signal_to_level(signal_name : String, callable : Callable) -> void:
|
||||
_try_connecting_signal_to_node(current_level, signal_name, callable)
|
||||
|
||||
func get_main_menu_scene_path() -> String:
|
||||
return main_menu_scene_path
|
||||
|
||||
func _load_main_menu() -> void:
|
||||
if scene_loader_node:
|
||||
scene_loader_node.load_scene(get_main_menu_scene_path())
|
||||
else:
|
||||
get_tree().change_scene_to_file(get_main_menu_scene_path())
|
||||
|
||||
func _find_in_scene_lister(level_path : String) -> int:
|
||||
if not scene_lister: return -1
|
||||
level_path = ResourceUID.ensure_path(level_path)
|
||||
return scene_lister.files.find(level_path)
|
||||
|
||||
func is_on_last_level() -> bool:
|
||||
var current_level_id = _find_in_scene_lister(current_level_path)
|
||||
return current_level_id > -1 and current_level_id == scene_lister.files.size() - 1
|
||||
|
||||
func get_relative_level_path(offset : int = 1) -> String:
|
||||
var current_level_id := _find_in_scene_lister(current_level_path)
|
||||
if current_level_id > -1:
|
||||
if current_level_id >= max(0, -(offset)) and current_level_id < scene_lister.files.size() - max(0, offset):
|
||||
current_level_id += offset
|
||||
return scene_lister.files[current_level_id]
|
||||
return ""
|
||||
|
||||
func get_next_level_path() -> String:
|
||||
return get_relative_level_path(1)
|
||||
|
||||
func get_prev_level_path() -> String:
|
||||
return get_relative_level_path(-1)
|
||||
|
||||
func get_ending_scene_path() -> String:
|
||||
return ending_scene_path
|
||||
|
||||
func _load_ending() -> void:
|
||||
if not get_ending_scene_path().is_empty():
|
||||
if scene_loader_node:
|
||||
scene_loader_node.load_scene(get_ending_scene_path())
|
||||
else:
|
||||
get_tree().change_scene_to_file(get_ending_scene_path())
|
||||
else:
|
||||
_load_main_menu()
|
||||
|
||||
func _on_level_lost() -> void:
|
||||
if level_lost_scene:
|
||||
var instance = level_lost_scene.instantiate()
|
||||
get_tree().current_scene.add_child(instance)
|
||||
_try_connecting_signal_to_node(instance, &"restart_pressed", _reload_level)
|
||||
_try_connecting_signal_to_node(instance, &"main_menu_pressed", _load_main_menu)
|
||||
else:
|
||||
_reload_level()
|
||||
|
||||
func get_checkpoint_level_path() -> String:
|
||||
if checkpoint_level_path.is_empty():
|
||||
if scene_lister:
|
||||
return scene_lister.files.front()
|
||||
if not starting_level_path.is_empty():
|
||||
return starting_level_path
|
||||
return checkpoint_level_path
|
||||
|
||||
func load_level(level_path : String) -> void:
|
||||
current_level_path = level_path
|
||||
level_loader.load_level(level_path)
|
||||
|
||||
func _load_checkpoint_level() -> void:
|
||||
load_level(get_checkpoint_level_path())
|
||||
|
||||
func _reload_level() -> void:
|
||||
load_level(current_level_path)
|
||||
|
||||
func _load_win_screen_or_ending() -> void:
|
||||
if game_won_scene:
|
||||
var instance = game_won_scene.instantiate()
|
||||
get_tree().current_scene.add_child(instance)
|
||||
_try_connecting_signal_to_node(instance, &"continue_pressed", _load_ending)
|
||||
_try_connecting_signal_to_node(instance, &"restart_pressed", _reload_level)
|
||||
_try_connecting_signal_to_node(instance, &"main_menu_pressed", _load_main_menu)
|
||||
else:
|
||||
_load_ending()
|
||||
|
||||
func _load_level_won_screen_or_checkpoint() -> void:
|
||||
if level_won_scene:
|
||||
var instance = level_won_scene.instantiate()
|
||||
get_tree().current_scene.add_child(instance)
|
||||
_try_connecting_signal_to_node(instance, &"continue_pressed", _load_checkpoint_level)
|
||||
_try_connecting_signal_to_node(instance, &"restart_pressed", _reload_level)
|
||||
_try_connecting_signal_to_node(instance, &"main_menu_pressed", _load_main_menu)
|
||||
else:
|
||||
_load_checkpoint_level()
|
||||
|
||||
func _on_level_won(next_level_path : String = ""):
|
||||
if next_level_path.is_empty():
|
||||
next_level_path = get_next_level_path()
|
||||
if next_level_path.is_empty():
|
||||
_load_win_screen_or_ending()
|
||||
else:
|
||||
checkpoint_level_path = next_level_path
|
||||
_load_level_won_screen_or_checkpoint()
|
||||
|
||||
func _on_level_changed(next_level_path : String):
|
||||
checkpoint_level_path = next_level_path
|
||||
_load_checkpoint_level()
|
||||
|
||||
func _connect_level_signals() -> void:
|
||||
_try_connecting_signal_to_level(&"level_lost", _on_level_lost)
|
||||
_try_connecting_signal_to_level(&"level_won", _on_level_won)
|
||||
_try_connecting_signal_to_level(&"level_changed", _on_level_changed)
|
||||
|
||||
func _on_level_loader_level_loaded() -> void:
|
||||
current_level = level_loader.current_level
|
||||
await current_level.ready
|
||||
_connect_level_signals()
|
||||
|
||||
func _on_level_loader_level_load_started() -> void:
|
||||
pass
|
||||
|
||||
func _on_level_loader_level_ready() -> void:
|
||||
pass
|
||||
|
||||
func _auto_load() -> void:
|
||||
if auto_load:
|
||||
_load_checkpoint_level()
|
||||
|
||||
func _ready() -> void:
|
||||
if Engine.is_editor_hint(): return
|
||||
level_loader.level_loaded.connect(_on_level_loader_level_loaded)
|
||||
level_loader.level_ready.connect(_on_level_loader_level_ready)
|
||||
level_loader.level_load_started.connect(_on_level_loader_level_load_started)
|
||||
_auto_load()
|
||||
@@ -0,0 +1 @@
|
||||
uid://bllg4gg7v1tsr
|
||||
23
addons/maaacks_game_template/extras/scripts/scene_lister.gd
Normal file
23
addons/maaacks_game_template/extras/scripts/scene_lister.gd
Normal file
@@ -0,0 +1,23 @@
|
||||
@tool
|
||||
extends Node
|
||||
class_name SceneLister
|
||||
## Helper class for listing all the scenes in a directory.
|
||||
|
||||
## List of paths to scene files.
|
||||
## Prefilled in the editor by selecting a directory.
|
||||
@export var files : Array[String]
|
||||
## Prefill files with any scenes in the directory.
|
||||
@export_dir var directory : String :
|
||||
set(value):
|
||||
directory = value
|
||||
_refresh_files()
|
||||
|
||||
func _refresh_files():
|
||||
if not is_inside_tree() or directory.is_empty(): return
|
||||
var dir_access = DirAccess.open(directory)
|
||||
if dir_access:
|
||||
files.clear()
|
||||
for file in dir_access.get_files():
|
||||
if not file.ends_with(".tscn"):
|
||||
continue
|
||||
files.append(directory + "/" + file)
|
||||
@@ -0,0 +1 @@
|
||||
uid://wjq7li836lwj
|
||||
@@ -0,0 +1,73 @@
|
||||
extends Node
|
||||
|
||||
## Path to a main menu scene.
|
||||
@export_file("*.tscn") var main_menu_scene_path : String
|
||||
## Optional path to an ending scene.
|
||||
@export_file("*.tscn") var ending_scene_path : String
|
||||
## Optional screen to be shown after the game is won.
|
||||
@export var game_won_scene : PackedScene
|
||||
## Optional screen to be shown after the game is lost.
|
||||
@export var game_lost_scene : PackedScene
|
||||
|
||||
## If Maaack's Scene Loader is installed, then it will be used to change scenes.
|
||||
@onready var scene_loader_node = get_tree().root.get_node_or_null(^"SceneLoader")
|
||||
|
||||
var has_lost_game : bool = false
|
||||
var has_won_game : bool = false
|
||||
|
||||
func _try_connecting_signal_to_node(node : Node, signal_name : String, callable : Callable) -> void:
|
||||
if node.has_signal(signal_name) and not node.is_connected(signal_name, callable):
|
||||
node.connect(signal_name, callable)
|
||||
|
||||
func get_main_menu_scene_path() -> String:
|
||||
return main_menu_scene_path
|
||||
|
||||
func _load_main_menu() -> void:
|
||||
if scene_loader_node:
|
||||
scene_loader_node.load_scene(get_main_menu_scene_path())
|
||||
else:
|
||||
get_tree().change_scene_to_file(get_main_menu_scene_path())
|
||||
|
||||
func get_ending_scene_path() -> String:
|
||||
return ending_scene_path
|
||||
|
||||
func _load_ending() -> void:
|
||||
if not get_ending_scene_path().is_empty():
|
||||
if scene_loader_node:
|
||||
scene_loader_node.load_scene(get_ending_scene_path())
|
||||
else:
|
||||
get_tree().change_scene_to_file(get_ending_scene_path())
|
||||
else:
|
||||
_load_main_menu()
|
||||
|
||||
func _load_lose_screen_or_reload() -> void:
|
||||
if game_lost_scene:
|
||||
var instance = game_lost_scene.instantiate()
|
||||
get_tree().current_scene.add_child(instance)
|
||||
_try_connecting_signal_to_node(instance, &"restart_pressed", _reload_level)
|
||||
_try_connecting_signal_to_node(instance, &"main_menu_pressed", _load_main_menu)
|
||||
else:
|
||||
_reload_level()
|
||||
|
||||
func _reload_level() -> void:
|
||||
get_tree().reload_current_scene()
|
||||
|
||||
func _load_win_screen_or_ending() -> void:
|
||||
if game_won_scene:
|
||||
var instance = game_won_scene.instantiate()
|
||||
get_tree().current_scene.add_child(instance)
|
||||
_try_connecting_signal_to_node(instance, &"continue_pressed", _load_ending)
|
||||
_try_connecting_signal_to_node(instance, &"restart_pressed", _reload_level)
|
||||
_try_connecting_signal_to_node(instance, &"main_menu_pressed", _load_main_menu)
|
||||
else:
|
||||
_load_ending()
|
||||
|
||||
func game_lost() -> void:
|
||||
if has_won_game or has_lost_game: return
|
||||
has_lost_game = true
|
||||
_load_lose_screen_or_reload()
|
||||
|
||||
func game_won() -> void:
|
||||
if has_won_game or has_lost_game: return
|
||||
has_won_game = true
|
||||
_load_win_screen_or_ending()
|
||||
@@ -0,0 +1 @@
|
||||
uid://bmlwpkrav3q56
|
||||
Reference in New Issue
Block a user