Skip to content

Commit

Permalink
Enable untyped declaration warning in project + type all content
Browse files Browse the repository at this point in the history
  • Loading branch information
froissant committed Apr 2, 2024
1 parent ba29cae commit 231244f
Show file tree
Hide file tree
Showing 12 changed files with 276 additions and 119 deletions.
4 changes: 4 additions & 0 deletions godot/project.godot
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ config/icon="res://icon.svg"

UserSettings="*res://settings/user_settings.gd"

[debug]

gdscript/warnings/untyped_declaration=1

[display]

window/size/viewport_width=576
Expand Down
158 changes: 158 additions & 0 deletions godot/save_game/save_game.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
## Script that manages saving games.
class_name SaveGame extends Node

const ENABLED = true
const ENCRYPTION_KEY = "godotrules"
const SAVE_GAME_TEMPLATE = "savegame.save"
const SAVE_GROUP_NAME = "Persist"
const NODE_DATA = "node_data"

static func delete_save() -> void:
if not ENABLED:
return

DirAccess.remove_absolute("user://" + SAVE_GAME_TEMPLATE)

static func has_save() -> bool:
return FileAccess.file_exists("user://" + SAVE_GAME_TEMPLATE)

static func save_game(tree:SceneTree) -> void:
if not ENABLED:
return

print("Saving game to user://" + SAVE_GAME_TEMPLATE)

var save_file: FileAccess = null

if OS.is_debug_build():
save_file = FileAccess.open("user://" + SAVE_GAME_TEMPLATE, FileAccess.WRITE)
else:
save_file = FileAccess.open_encrypted_with_pass("user://" + SAVE_GAME_TEMPLATE, FileAccess.WRITE, ENCRYPTION_KEY)

var save_nodes: Array[Node] = tree.get_nodes_in_group(SAVE_GROUP_NAME)

for node in save_nodes:
var save_data: Dictionary = {}

# Check the node is an instanced scene so it can be instanced again during load.
if not node.scene_file_path.is_empty():
save_data["scene_file_path"] = node.scene_file_path

if not node.get_path().is_empty():
save_data["path"] = node.get_path()

if not node.get_parent().get_path().is_empty():
save_data["parent"] = node.get_parent().get_path()

if "position" in node:
save_data["pos_x"] = node.position.x
save_data["pos_y"] = node.position.y
if node.position is Vector3:
save_data["pos_z"] = node.position.z

if node is Node2D:
save_data["rotation"] = node.rotation
elif node is Node3D:
save_data["rotation_x"] = node.rotation.x
save_data["rotation_y"] = node.rotation.y
save_data["rotation_z"] = node.rotation.z

if "scale" in node:
save_data["scale_x"] = node.scale.x
save_data["scale_y"] = node.scale.y
if node.scale is Vector3:
save_data["scale_z"] = node.scale.z

save_data["visible"] = node.visible

if node is CanvasItem:
save_data["modulate_r"] = node.modulate.r
save_data["modulate_g"] = node.modulate.g
save_data["modulate_b"] = node.modulate.b
save_data["modulate_a"] = node.modulate.a

# Call the node's save function.
if node.has_method("save_data"):
save_data["node_data"] = node.call("save_data")

# Store the save dictionary as a new line in the save file.
save_file.store_line(JSON.stringify(save_data))

static func load_game(tree:SceneTree) -> void:
if not ENABLED:
return

if not has_save():
print("No save game found. Skipped loading!")
return

print("Load game from user://" + SAVE_GAME_TEMPLATE)

var save_nodes: Array[Node] = tree.get_nodes_in_group(SAVE_GROUP_NAME)

var nodes_by_path: Dictionary = {}
for node in save_nodes:
if not node.get_path().is_empty():
nodes_by_path[node.get_path()] = node

# Load the file line by line and process that dictionary to restore
# the object it represents.
var save_file: FileAccess = null

if OS.is_debug_build():
save_file = FileAccess.open("user://" + SAVE_GAME_TEMPLATE, FileAccess.READ)
else:
save_file = FileAccess.open_encrypted_with_pass("user://" + SAVE_GAME_TEMPLATE, FileAccess.READ, ENCRYPTION_KEY)

while save_file.get_position() < save_file.get_length():
# Get the saved dictionary from the next line in the save file
var test_json_conv: JSON = JSON.new()
test_json_conv.parse(save_file.get_line())
var save_data: Variant = test_json_conv.get_data()

# Firstly, we need to create the object and add it to the tree and set its position.
var node: Node = null

if "path" in save_data and nodes_by_path.has(NodePath(save_data.path)):
node = nodes_by_path[NodePath(save_data.path)]
nodes_by_path.erase(NodePath(save_data.path))
elif "path" in save_data and "parent" in save_data and "scene_file_path" in save_data:
# node is not present in tree so it was dynamically added at runtime
var parent: Node = tree.root.get_node(NodePath(save_data["parent"]))
node = load(save_data["scene_file_path"]).instantiate()
parent.add_child(node)
else:
push_warning("skipping loading node from save game: node got moved.")
continue

if "position" in node:
if node.scale is Vector2:
node.position = Vector2(save_data["pos_x"], save_data["pos_y"])
elif node.scale is Vector3:
node.position = Vector3(save_data["pos_x"], save_data["pos_y"], save_data["pos_z"])

if node is Node2D:
node.rotation = save_data["rotation"]
elif node is Node3D:
node.rotation = Vector3(save_data["rotation_x"], save_data["rotation_y"], save_data["rotation_z"])

if "scale" in node:
if node.scale is Vector2:
node.scale = Vector2(save_data["scale_x"], save_data["scale_y"])
elif node.scale is Vector3:
node.scale = Vector3(save_data["scale_x"], save_data["scale_y"], save_data["scale_z"])

if save_data.has("visible") and "visible" in node:
node.visible = save_data["visible"]

if node is CanvasItem:
node.modulate = Color(save_data["modulate_r"], save_data["modulate_g"], save_data["modulate_b"], save_data["modulate_a"])

if node.has_method("load_data") and save_data.has("node_data"):
node.call("load_data", save_data["node_data"])

# delete any node from scene that was not persisted into the save file
# but is currently tagged as "Persisted" -> this means node got removed in the meantime
for key: String in nodes_by_path:
var node: Node = nodes_by_path[key]
node.queue_free()
30 changes: 15 additions & 15 deletions godot/scenes/boot/bootsplash_scene.gd
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
class_name BootsplashScene
extends Control

@export var fade_duration:float = 1.0
@export var stay_duration:float = 1.5
@export var node:PackedScene
@export var next_scene:PackedScene
@export var interuptable:bool = true
@export var fade_duration: float = 1.0
@export var stay_duration: float = 1.5
@export var node: PackedScene
@export var next_scene: PackedScene
@export var interuptable: bool = true

@onready var control = %NodeContainer
@onready var instance:Node2D = node.instantiate()
@onready var control: Control = %NodeContainer
@onready var instance: Node2D = node.instantiate()

func _ready():
func _ready() -> void:
instance.modulate.a = 0.0
control.add_child(instance)
var tween = create_tween()
var tween: Tween = create_tween()
tween.set_trans(Tween.TRANS_CUBIC)
tween.set_ease(Tween.EASE_IN)
tween.tween_property(instance, "modulate:a", 1.0, fade_duration)\
.from(0.0)\
.finished.connect(_fade_out)
func _process(_delta):

func _process(_delta: float) -> void:
if interuptable and Input.is_action_just_pressed("exit"):
_change_scene()
func _fade_out():
var tween = create_tween()

func _fade_out() -> void:
var tween: Tween = create_tween()
tween.set_trans(Tween.TRANS_CUBIC)
tween.set_ease(Tween.EASE_IN)
tween.tween_property(instance, "modulate:a", 0.0, fade_duration)\
.set_delay(stay_duration)\
.finished.connect(_change_scene)

func _change_scene():
func _change_scene() -> void:
get_tree().change_scene_to_packed(next_scene)
12 changes: 6 additions & 6 deletions godot/scenes/game_settings_scene.gd
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
extends Node2D

@onready var overlay := %FadeOverlay
@onready var return_button := %ReturnButton
@onready var overlay: FadeOverlay = %FadeOverlay
@onready var return_button: Button = %ReturnButton

func _ready():
func _ready() -> void:
overlay.on_complete_fade_out.connect(_on_fade_overlay_on_complete_fade_out)
return_button.pressed.connect(_on_return_button_pressed)

overlay.visible = true
return_button.grab_focus()

func _on_fade_overlay_on_complete_fade_out():
func _on_fade_overlay_on_complete_fade_out() -> void:
get_tree().change_scene_to_file("res://scenes/main_menu_scene.tscn")

func _on_return_button_pressed():
func _on_return_button_pressed() -> void:
overlay.fade_out()
12 changes: 6 additions & 6 deletions godot/scenes/ingame_scene.gd
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
extends Node2D

@onready var fade_overlay = %FadeOverlay
@onready var pause_overlay = %PauseOverlay
@onready var fade_overlay: FadeOverlay = %FadeOverlay
@onready var pause_overlay: Control = %PauseOverlay

func _ready() -> void:
fade_overlay.visible = true

if SaveGame.has_save():
SaveGame.load_game(get_tree())

pause_overlay.game_exited.connect(_save_game)

func _input(event) -> void:
func _input(event: InputEvent) -> void:
if event.is_action_pressed("pause") and not pause_overlay.visible:
get_viewport().set_input_as_handled()
get_tree().paused = true
pause_overlay.grab_button_focus()
pause_overlay.visible = true

func _save_game() -> void:
SaveGame.save_game(get_tree())
26 changes: 13 additions & 13 deletions godot/scenes/main_menu_scene.gd
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
extends Node2D

@export var game_scene:PackedScene
@export var settings_scene:PackedScene
@export var game_scene: PackedScene
@export var settings_scene: PackedScene

@onready var overlay := %FadeOverlay
@onready var continue_button := %ContinueButton
@onready var new_game_button := %NewGameButton
@onready var settings_button := %SettingsButton
@onready var exit_button := %ExitButton
@onready var overlay: FadeOverlay = %FadeOverlay
@onready var continue_button: Button = %ContinueButton
@onready var new_game_button: Button = %NewGameButton
@onready var settings_button: Button = %SettingsButton
@onready var exit_button: Button = %ExitButton

var next_scene = game_scene
var new_game = true
var next_scene: PackedScene = game_scene
var new_game: bool = true

func _ready() -> void:
overlay.visible = true
new_game_button.disabled = game_scene == null
settings_button.disabled = settings_scene == null
continue_button.visible = SaveGame.has_save() and SaveGame.ENABLED

# connect signals
new_game_button.pressed.connect(_on_play_button_pressed)
continue_button.pressed.connect(_on_continue_button_pressed)
settings_button.pressed.connect(_on_settings_button_pressed)
exit_button.pressed.connect(_on_exit_button_pressed)
overlay.on_complete_fade_out.connect(_on_fade_overlay_on_complete_fade_out)

if continue_button.visible:
continue_button.grab_focus()
else:
Expand All @@ -34,11 +34,11 @@ func _on_settings_button_pressed() -> void:
new_game = false
next_scene = settings_scene
overlay.fade_out()

func _on_play_button_pressed() -> void:
next_scene = game_scene
overlay.fade_out()

func _on_continue_button_pressed() -> void:
new_game = false
next_scene = game_scene
Expand Down
8 changes: 4 additions & 4 deletions godot/scenes/node_example.gd
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ extends Sprite2D
# specify in nodes to load data
# from save game for this node
func load_data(data:Dictionary) -> void:
print(data.hello)
print(data.hello)

# specify in nodes to save data
# of this node to the save game
func save_data() -> Dictionary:
return {
"hello": "Hello Godot!"
}
func _process(delta):

func _process(delta: float) -> void:
if Input.is_action_pressed("ui_right"):
position.x += 100 * delta
if Input.is_action_pressed("ui_left"):
Expand Down
Loading

0 comments on commit 231244f

Please sign in to comment.