rewired signals and added savemanager and resource saving
This commit is contained in:
parent
0f5045dc22
commit
f694d5c4f8
13 changed files with 138 additions and 87 deletions
|
|
@ -1,12 +1,23 @@
|
||||||
extends CharacterBody3D
|
extends CharacterBody3D
|
||||||
|
|
||||||
@onready var animated_mesh = $GobotSkin
|
@onready var animated_mesh = $GobotSkin
|
||||||
|
|
||||||
var in_battle: bool = false
|
var in_battle: bool = false
|
||||||
|
|
||||||
const SPEED = 5.0
|
const SPEED = 5.0
|
||||||
const JUMP_VELOCITY = 4.5
|
const JUMP_VELOCITY = 4.5
|
||||||
|
|
||||||
|
func on_save_game(saved_data : Array[SavedData]):
|
||||||
|
var data = SavedData.new()
|
||||||
|
data.position = global_position
|
||||||
|
data.scene_path = scene_file_path
|
||||||
|
saved_data.append(data)
|
||||||
|
|
||||||
|
func on_before_load_game():
|
||||||
|
get_parent().remove_child(self)
|
||||||
|
queue_free()
|
||||||
|
|
||||||
|
func on_load_game(saved_data: SavedData):
|
||||||
|
global_position = saved_data.position
|
||||||
|
|
||||||
func _physics_process(delta: float) -> void:
|
func _physics_process(delta: float) -> void:
|
||||||
if in_battle == true:
|
if in_battle == true:
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_xuba7"]
|
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_xuba7"]
|
||||||
height = 1.7
|
height = 1.7
|
||||||
|
|
||||||
[node name="Player" type="CharacterBody3D"]
|
[node name="Player" type="CharacterBody3D" groups=["save_nodes"]]
|
||||||
axis_lock_angular_x = true
|
axis_lock_angular_x = true
|
||||||
axis_lock_angular_y = true
|
axis_lock_angular_y = true
|
||||||
axis_lock_angular_z = true
|
axis_lock_angular_z = true
|
||||||
|
|
|
||||||
20
game.gd
20
game.gd
|
|
@ -10,14 +10,17 @@ func _ready() -> void:
|
||||||
if skip_menu == true:
|
if skip_menu == true:
|
||||||
UI.show_ingame_controls(!skip_menu)
|
UI.show_ingame_controls(!skip_menu)
|
||||||
UI.show_main_menu(!skip_menu)
|
UI.show_main_menu(!skip_menu)
|
||||||
_on_ui_on_new_game_started()
|
start_new_game()
|
||||||
return
|
return
|
||||||
|
|
||||||
# On start of the game, the main menu is shown
|
# On start of the game, the main menu is shown
|
||||||
|
UI.connect("on_new_game_started", start_new_game)
|
||||||
|
UI.connect("on_game_continued", continue_game)
|
||||||
UI.show_main_menu()
|
UI.show_main_menu()
|
||||||
|
|
||||||
|
|
||||||
# this event comes from the MainMenu Node in the UI
|
# this event comes from the MainMenu Node in the UI
|
||||||
func _on_ui_on_new_game_started() -> void:
|
func start_new_game() -> void:
|
||||||
var world = preload("res://worlds/debug_level.tscn").instantiate()
|
var world = preload("res://worlds/debug_level.tscn").instantiate()
|
||||||
UI.show_ingame_controls()
|
UI.show_ingame_controls()
|
||||||
|
|
||||||
|
|
@ -27,8 +30,17 @@ func _on_ui_on_new_game_started() -> void:
|
||||||
# Add the starting player to the starting world
|
# Add the starting player to the starting world
|
||||||
var player = preload("res://entities/player/player.tscn").instantiate()
|
var player = preload("res://entities/player/player.tscn").instantiate()
|
||||||
world.add_child(player)
|
world.add_child(player)
|
||||||
|
|
||||||
|
# Give the player a monster
|
||||||
|
var monster = MonsterData.new()
|
||||||
|
monster.set_data("debuggy")
|
||||||
|
SaveManager.current_save.party.push_back(monster)
|
||||||
|
|
||||||
|
# Update the UI once
|
||||||
|
UI.update()
|
||||||
|
|
||||||
# event comes from the MainMenu Node in the UI
|
# event comes from the MainMenu Node in the UI
|
||||||
func _on_ui_on_game_continued() -> void:
|
func continue_game() -> void:
|
||||||
SaveGame.load()
|
SaveManager.load_game()
|
||||||
UI.show_ingame_controls()
|
UI.show_ingame_controls()
|
||||||
|
UI.update()
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
[node name="Game" type="Node3D"]
|
[node name="Game" type="Node3D"]
|
||||||
script = ExtResource("1_mfbtr")
|
script = ExtResource("1_mfbtr")
|
||||||
|
skip_menu = false
|
||||||
|
|
||||||
[node name="UI" parent="." instance=ExtResource("2_ynhuf")]
|
[node name="UI" parent="." instance=ExtResource("2_ynhuf")]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
extends Node
|
|
||||||
# save_game.gd - Responsible for saving and loading the game using a SaveData resource
|
|
||||||
|
|
||||||
const SAVE_GAME_PATH := "user://savegame.save"
|
|
||||||
const VERSION: int = 1
|
|
||||||
|
|
||||||
func save() -> void:
|
|
||||||
var save_file = FileAccess.open(SAVE_GAME_PATH, FileAccess.WRITE)
|
|
||||||
|
|
||||||
# Save the meta data like save game version and timestamps
|
|
||||||
var meta_data = {
|
|
||||||
"meta": {
|
|
||||||
"version" : VERSION,
|
|
||||||
"timestamp" : Time.get_datetime_string_from_system()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var meta_json_string = JSON.stringify(meta_data)
|
|
||||||
save_file.store_line(meta_json_string)
|
|
||||||
|
|
||||||
# Save the player node's position
|
|
||||||
var player = get_tree().get_root().find_child("Player", true, false)
|
|
||||||
var player_node_data = player.save()
|
|
||||||
var player_json = JSON.stringify(player_node_data)
|
|
||||||
save_file.store_line(player_json)
|
|
||||||
|
|
||||||
# Save all other stuff from the SaveData
|
|
||||||
var save_data = SaveData.call("save")
|
|
||||||
var json_string = JSON.stringify(save_data)
|
|
||||||
save_file.store_line(json_string)
|
|
||||||
|
|
||||||
func load() -> void:
|
|
||||||
if not FileAccess.file_exists(SAVE_GAME_PATH):
|
|
||||||
print("No save file exists.")
|
|
||||||
return
|
|
||||||
|
|
||||||
# NEXT STEP: How to load correctly and in which order?
|
|
||||||
# first the current world, then the player and their position and then the inventory?
|
|
||||||
|
|
||||||
# Load the save file line by line and process the dictionary to restore
|
|
||||||
# the object it represents
|
|
||||||
var save_file = FileAccess.open(SAVE_GAME_PATH, FileAccess.READ)
|
|
||||||
while save_file.get_position() < save_file.get_length():
|
|
||||||
var json_string = save_file.get_line()
|
|
||||||
|
|
||||||
# Creates the helper class to interact with JSON
|
|
||||||
var json = JSON.new()
|
|
||||||
|
|
||||||
# Check if there is any error while parsing the JSON string, skip in case of failure
|
|
||||||
var parse_result = json.parse(json_string)
|
|
||||||
if not parse_result == OK:
|
|
||||||
print("JSON Parse Error: ", json.get_error_message(), " in ", json_string, " at line", json.get_error_line())
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Get the data from the JSON object
|
|
||||||
var node_data = json.get_data()
|
|
||||||
|
|
||||||
if node_data.has("meta"):
|
|
||||||
print("Meta data ignored.")
|
|
||||||
|
|
||||||
# handle the player data (name, money, etc.)
|
|
||||||
if node_data.has("player_data"):
|
|
||||||
var world_to_load = load(node_data["player_data"].world)
|
|
||||||
var instance = world_to_load.instantiate()
|
|
||||||
instance.add_child(load("res://scenes/player/player.tscn").instantiate())
|
|
||||||
var level_container = get_tree().get_root().find_child("CurrentLevel", true, false)
|
|
||||||
level_container.add_child(instance)
|
|
||||||
|
|
||||||
|
|
||||||
func savegame_exists() -> bool:
|
|
||||||
return FileAccess.file_exists(SAVE_GAME_PATH)
|
|
||||||
|
|
@ -18,9 +18,8 @@ config/icon="res://assets/logo/logo.png"
|
||||||
|
|
||||||
[autoload]
|
[autoload]
|
||||||
|
|
||||||
|
SaveManager="*res://resources/save_game/save_manager.gd"
|
||||||
UI="*res://ui/ui.gd"
|
UI="*res://ui/ui.gd"
|
||||||
SaveGame="*res://globals/save_game.gd"
|
|
||||||
SaveData="*res://resources/save_data.gd"
|
|
||||||
|
|
||||||
[display]
|
[display]
|
||||||
|
|
||||||
|
|
@ -35,7 +34,6 @@ window/handheld/orientation=1
|
||||||
folder_colors={
|
folder_colors={
|
||||||
"res://assets/": "red",
|
"res://assets/": "red",
|
||||||
"res://entities/": "blue",
|
"res://entities/": "blue",
|
||||||
"res://globals/": "gray",
|
|
||||||
"res://materials/": "pink",
|
"res://materials/": "pink",
|
||||||
"res://resources/": "yellow",
|
"res://resources/": "yellow",
|
||||||
"res://ui/": "green"
|
"res://ui/": "green"
|
||||||
|
|
@ -43,7 +41,7 @@ folder_colors={
|
||||||
|
|
||||||
[global_group]
|
[global_group]
|
||||||
|
|
||||||
Persist="Contains all nodes that need to be saved"
|
save_nodes=""
|
||||||
|
|
||||||
[input]
|
[input]
|
||||||
|
|
||||||
|
|
|
||||||
15
resources/save_game/save_game.gd
Normal file
15
resources/save_game/save_game.gd
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
class_name SavedGame
|
||||||
|
extends Resource
|
||||||
|
# This resource collects all data to save, from the player and other nodes
|
||||||
|
|
||||||
|
# Player info to save
|
||||||
|
@export var player_money : int = 0
|
||||||
|
@export var party: Array[MonsterData] = []
|
||||||
|
|
||||||
|
# Environment info to save (daytime, current world, weather)
|
||||||
|
@export var world : String = ""
|
||||||
|
@export var daytime : float = 12.0
|
||||||
|
@export var weather : String = "this should be a global enum"
|
||||||
|
|
||||||
|
# All other nodes's data is collected here
|
||||||
|
@export var saved_data: Array[SavedData] = []
|
||||||
74
resources/save_game/save_manager.gd
Normal file
74
resources/save_game/save_manager.gd
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
# The group of nodes to save is named "save_nodes"
|
||||||
|
|
||||||
|
# Reference to the currently loaded save game
|
||||||
|
@onready var current_save : SavedGame = SavedGame.new()
|
||||||
|
|
||||||
|
var world
|
||||||
|
|
||||||
|
func save_game():
|
||||||
|
# Create a new empty save game
|
||||||
|
var saved_game := SavedGame.new()
|
||||||
|
|
||||||
|
# Save the environment data
|
||||||
|
saved_game.world = "res://worlds/debug_level.tscn" #SceneManager.get_world()
|
||||||
|
saved_game.daytime = 12.5
|
||||||
|
saved_game.weather = "this should be an enum"
|
||||||
|
|
||||||
|
# Save the party data
|
||||||
|
for monster in SaveManager.current_save.party:
|
||||||
|
saved_game.party.append(monster)
|
||||||
|
|
||||||
|
# Create an array to store the data of all other nodes
|
||||||
|
var saved_data : Array[SavedData] = []
|
||||||
|
|
||||||
|
# Use a group call to collect all the data from the other objects
|
||||||
|
get_tree().call_group("save_nodes", "on_save_game", saved_data)
|
||||||
|
|
||||||
|
# Store the array in the saved game
|
||||||
|
saved_game.saved_data = saved_data
|
||||||
|
|
||||||
|
# Store it into a file
|
||||||
|
ResourceSaver.save(saved_game, "user://savegame.tres")
|
||||||
|
|
||||||
|
|
||||||
|
func load_game():
|
||||||
|
# Load the saved game file
|
||||||
|
var saved_game : SavedGame = load("user://savegame.tres") as SavedGame
|
||||||
|
|
||||||
|
# Return if no save file is found
|
||||||
|
if saved_game == null:
|
||||||
|
print("No savegame found.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Save a reference to the current save game
|
||||||
|
current_save = saved_game
|
||||||
|
print(saved_game.party[0].display_name)
|
||||||
|
|
||||||
|
# Clean up all non-static objects from the scene
|
||||||
|
get_tree().call_group("save_nodes", "on_before_load_game")
|
||||||
|
|
||||||
|
# Restore world data
|
||||||
|
# doing it via get_tree, until the SceneManager exists
|
||||||
|
var _world : Node = load(saved_game.world).instantiate()
|
||||||
|
get_node("/root/Game/CurrentLevel").add_child(_world)
|
||||||
|
world = _world
|
||||||
|
#SceneManager.set_world(saved_game.world)
|
||||||
|
|
||||||
|
# Restore all other nodes
|
||||||
|
for node in saved_game.saved_data:
|
||||||
|
# Instantiate the scene for the node
|
||||||
|
var scene := load(node.scene_path) as PackedScene
|
||||||
|
var restored_node = scene.instantiate()
|
||||||
|
|
||||||
|
# Add the item to the world
|
||||||
|
# todo: add parent as value to save
|
||||||
|
world.add_child(restored_node)
|
||||||
|
|
||||||
|
# run a callback to restore the item's state
|
||||||
|
if restored_node.has_method("on_load_game"):
|
||||||
|
restored_node.on_load_game(node)
|
||||||
|
|
||||||
|
func save_exists():
|
||||||
|
return FileAccess.file_exists("user://savegame.tres")
|
||||||
10
resources/save_game/saved_data.gd
Normal file
10
resources/save_game/saved_data.gd
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
class_name SavedData
|
||||||
|
extends Resource
|
||||||
|
# Main class that can save a node's information
|
||||||
|
# Every node's save data extends from this class
|
||||||
|
|
||||||
|
# Position of the node to save
|
||||||
|
@export var position : Vector3 = Vector3.ZERO
|
||||||
|
|
||||||
|
# Path to the scene of the node
|
||||||
|
@export var scene_path : String = ""
|
||||||
|
|
@ -19,7 +19,7 @@ func _process(delta) -> void:
|
||||||
save_timer = 0.0
|
save_timer = 0.0
|
||||||
|
|
||||||
# Save the game
|
# Save the game
|
||||||
SaveGame.save()
|
SaveManager.save_game()
|
||||||
|
|
||||||
save_button_held_down = false
|
save_button_held_down = false
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,12 @@ func _on_close_button_pressed() -> void:
|
||||||
pass # Replace with function body.
|
pass # Replace with function body.
|
||||||
|
|
||||||
func _update_monster_list() -> void:
|
func _update_monster_list() -> void:
|
||||||
for i in SaveData.monsters.size():
|
for i in SaveManager.current_save.party.size():
|
||||||
var monster = SaveData.monsters[i]
|
var monster = SaveManager.current_save.party[i]
|
||||||
var entry = monster_list_entry.instantiate()
|
var entry = monster_list_entry.instantiate()
|
||||||
|
|
||||||
# populate the new entry
|
# populate the new entry
|
||||||
entry.populate(SaveData.monsters[i])
|
entry.populate(SaveManager.current_save.party[i])
|
||||||
|
|
||||||
# add it to the list
|
# add it to the list
|
||||||
monster_list_entry_container.add_child(entry)
|
monster_list_entry_container.add_child(entry)
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ signal on_game_continued
|
||||||
|
|
||||||
# On ready, check which buttons to show
|
# On ready, check which buttons to show
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
if SaveGame.savegame_exists():
|
if SaveManager.save_exists():
|
||||||
%ContinueGameButton.visible = true
|
%ContinueGameButton.visible = true
|
||||||
%NewGameButton.visible = false
|
%NewGameButton.visible = false
|
||||||
|
|
||||||
|
|
|
||||||
2
ui/ui.gd
2
ui/ui.gd
|
|
@ -34,7 +34,7 @@ func _on_ingame_controls_menu_button_clicked() -> void:
|
||||||
ingame_menu.visible = true
|
ingame_menu.visible = true
|
||||||
|
|
||||||
# Updates the whole UI
|
# Updates the whole UI
|
||||||
func _update_ui() -> void:
|
func update() -> void:
|
||||||
# Updating the player info
|
# Updating the player info
|
||||||
ingame_menu._update_player_info()
|
ingame_menu._update_player_info()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue