gd: added menu template
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,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,9 @@
|
||||
class_name CaptureMouse
|
||||
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
|
@ -0,0 +1,48 @@
|
||||
@tool
|
||||
class_name LevelListLoader
|
||||
extends SceneLister
|
||||
## Extends [SceneLister] to manage level advancement through [GameStateExample].
|
||||
|
||||
signal level_load_started
|
||||
signal level_loaded
|
||||
signal levels_finished
|
||||
|
||||
## Container where the level instance will be added.
|
||||
@export var level_container : Node
|
||||
|
||||
var current_level : Node
|
||||
|
||||
func get_level_file(level_id : int):
|
||||
if files.is_empty():
|
||||
push_error("levels list is empty")
|
||||
return
|
||||
if level_id >= files.size():
|
||||
push_error("level_id is out of bounds of the levels list")
|
||||
level_id = files.size() - 1
|
||||
return files[level_id]
|
||||
|
||||
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_id : int):
|
||||
if is_instance_valid(current_level):
|
||||
current_level.queue_free()
|
||||
await current_level.tree_exited
|
||||
current_level = null
|
||||
var level_file = get_level_file(level_id)
|
||||
if level_file == null:
|
||||
levels_finished.emit()
|
||||
return
|
||||
SceneLoader.load_scene(level_file, true)
|
||||
level_load_started.emit()
|
||||
await SceneLoader.scene_loaded
|
||||
current_level = _attach_level(SceneLoader.get_resource())
|
||||
level_loaded.emit()
|
||||
|
||||
func _ready():
|
||||
if Engine.is_editor_hint():
|
||||
# Text files get a `.remap` extension added on export.
|
||||
_refresh_files()
|
@ -0,0 +1 @@
|
||||
uid://crbo2e4ndbyvk
|
@ -0,0 +1,143 @@
|
||||
class_name LevelListManager
|
||||
extends Node
|
||||
## Manager of level progress and the result screens between them.
|
||||
##
|
||||
## A helper script to assign to a node in a scene.
|
||||
## It works with a level list loader and a loading screen
|
||||
## to advance levels and open menus when players win or lose.
|
||||
|
||||
## Required reference to a level list loader in the scene.
|
||||
@export var level_list_loader : LevelListLoader
|
||||
## Required path to a main menu scene.
|
||||
@export_file("*.tscn") var main_menu_scene : String
|
||||
## Optional path to an ending scene.
|
||||
@export_file("*.tscn") var ending_scene : String
|
||||
@export var auto_load : bool = true
|
||||
@export_group("Screens")
|
||||
## Optional reference to a loading screen in the scene.
|
||||
@export var level_loading_screen : LoadingScreen
|
||||
## Optional win screen to be shown after the last level is won.
|
||||
@export var game_won_scene : PackedScene
|
||||
## Optional lose screen to be shown after the level is lost.
|
||||
@export var level_lost_scene : PackedScene
|
||||
## Optional level compete screen to be shown after the level is won.
|
||||
@export var level_won_scene : PackedScene
|
||||
## Loads a level on start.
|
||||
@export_group("Debugging")
|
||||
@export var force_level : int = -1
|
||||
|
||||
## Reference to the current level node.
|
||||
var current_level
|
||||
var current_level_id : int :
|
||||
set = set_current_level_id
|
||||
|
||||
func set_current_level_id(value : int) -> void:
|
||||
current_level_id = 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 _load_main_menu() -> void:
|
||||
SceneLoader.load_scene(main_menu_scene)
|
||||
|
||||
func _advance_level() -> bool:
|
||||
if is_on_last_level(): return false
|
||||
current_level_id += 1
|
||||
return true
|
||||
|
||||
func _advance_and_load_main_menu() -> void:
|
||||
_advance_level()
|
||||
_load_main_menu()
|
||||
|
||||
func _load_ending() -> void:
|
||||
if ending_scene:
|
||||
SceneLoader.load_scene(ending_scene)
|
||||
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_current_level_id() -> int:
|
||||
return current_level_id if force_level == -1 else force_level
|
||||
|
||||
func load_current_level() -> void:
|
||||
level_list_loader.load_level(get_current_level_id())
|
||||
|
||||
func _advance_and_reload() -> void:
|
||||
var _prior_level_id = get_current_level_id()
|
||||
_advance_level()
|
||||
current_level_id = _prior_level_id
|
||||
load_current_level()
|
||||
|
||||
func _load_next_level() -> void:
|
||||
_advance_level()
|
||||
load_current_level()
|
||||
|
||||
func _reload_level() -> void:
|
||||
load_current_level()
|
||||
|
||||
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_complete_screen_or_next_level() -> 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_next_level)
|
||||
_try_connecting_signal_to_node(instance, &"restart_pressed", _advance_and_reload)
|
||||
_try_connecting_signal_to_node(instance, &"main_menu_pressed", _advance_and_load_main_menu)
|
||||
else:
|
||||
_load_next_level()
|
||||
|
||||
func is_on_last_level() -> bool:
|
||||
return get_current_level_id() + 1 >= level_list_loader.files.size()
|
||||
|
||||
func _on_level_won():
|
||||
if is_on_last_level():
|
||||
_load_win_screen_or_ending()
|
||||
else:
|
||||
_load_level_complete_screen_or_next_level()
|
||||
|
||||
func _connect_level_signals() -> void:
|
||||
_try_connecting_signal_to_level(&"level_won", _on_level_won)
|
||||
_try_connecting_signal_to_level(&"level_lost", _on_level_lost)
|
||||
_try_connecting_signal_to_level(&"level_skipped", _load_next_level)
|
||||
|
||||
func _on_level_loader_level_loaded() -> void:
|
||||
current_level = level_list_loader.current_level
|
||||
await current_level.ready
|
||||
_connect_level_signals()
|
||||
if level_loading_screen:
|
||||
level_loading_screen.close()
|
||||
|
||||
func _on_level_loader_levels_finished() -> void:
|
||||
_load_win_screen_or_ending()
|
||||
|
||||
func _on_level_loader_level_load_started() -> void:
|
||||
if level_loading_screen:
|
||||
level_loading_screen.reset()
|
||||
|
||||
func _ready() -> void:
|
||||
level_list_loader.level_loaded.connect(_on_level_loader_level_loaded)
|
||||
level_list_loader.levels_finished.connect(_on_level_loader_levels_finished)
|
||||
level_list_loader.level_load_started.connect(_on_level_loader_level_load_started)
|
||||
if auto_load:
|
||||
load_current_level()
|
@ -0,0 +1 @@
|
||||
uid://yakisxcmflgk
|
52
addons/maaacks_game_template/extras/scripts/level_loader.gd
Normal file
52
addons/maaacks_game_template/extras/scripts/level_loader.gd
Normal file
@ -0,0 +1,52 @@
|
||||
@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
|
||||
## Loads a level on start.
|
||||
@export var auto_load : bool = false
|
||||
@export var current_level_path : String
|
||||
@export_group("Debugging")
|
||||
@export var force_level : String
|
||||
@export var current_level : Node
|
||||
|
||||
var is_loading : bool = false
|
||||
|
||||
func get_current_level_path() -> String:
|
||||
return current_level_path if force_level.is_empty() else force_level
|
||||
|
||||
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 = get_current_level_path()):
|
||||
if is_loading : return
|
||||
if is_instance_valid(current_level):
|
||||
current_level.queue_free()
|
||||
await current_level.tree_exited
|
||||
current_level = null
|
||||
is_loading = true
|
||||
current_level_path = level_path
|
||||
SceneLoader.load_scene(level_path, true)
|
||||
level_load_started.emit()
|
||||
await SceneLoader.scene_loaded
|
||||
is_loading = false
|
||||
current_level = _attach_level(SceneLoader.get_resource())
|
||||
level_loaded.emit()
|
||||
await current_level.ready
|
||||
level_ready.emit()
|
||||
|
||||
func reload_level():
|
||||
load_level()
|
||||
|
||||
func _ready():
|
||||
if auto_load:
|
||||
load_level()
|
@ -0,0 +1 @@
|
||||
uid://bbymrin0cm704
|
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
|
Reference in New Issue
Block a user