Godot Version
4.6.2
Question
Hi, I’m working on a text-based adventure game and I’m running into an issue when I try to save and then load the current room the player is in. Currently I have a main panel where most of the information is (on the left of the following image) and a side panel that always has the information of the current room the player is in and updates automatically when the room changes or when something changes in their current room (on the right of the image). Specifically this side_panel is where the problems start happening when saving.
After saving and loading, if I move to another room I get the following error in the side_panel.gd script (Invalid access to property or key ‘room_name’ on a base object of type ‘Nil’.). I noticed when loading the game as well it never updates the side_panel to the correct information.
Any help on the matter would be greatly appreciated. Thank you!
The following is my side_panel.gd script. The error is happening on line 12 of the script. Specifically the line that says room_name.text = new_room.room_name.
extends PanelContainer
class_name side_panel
@onready var room_name = $MarginContainer/Rows/Title/RoomName
@onready var room_description = $MarginContainer/Rows/RoomDescription
@onready var exit = $MarginContainer/Rows/List/Exits
@onready var npcs = $MarginContainer/Rows/List/NPCs
@onready var items = $MarginContainer/Rows/List/Items
func handle_room_changed(new_room):
room_name.text = new_room.room_name #This is the error line.
room_description.text = new_room.room_description
exit.text = new_room.exit_desc()
var npc_s = new_room.npc_desc()
if npc_s == "":
npcs.hide()
else:
npcs.show()
npcs.bbcode_text = new_room.npc_desc()
var items_s = new_room.item_desc()
if items_s == "":
items.hide()
else:
items.show()
items.bbcode_text = new_room.item_desc()
func handle_room_updated(cur_room):
handle_room_changed(cur_room)
This is my script for saving/loading.
extends Node
## Called when the node enters the scene tree for the first time.
func _ready() -> void:
_load()
const FILE_PATH: String = "user://SaveFile.json"
var save_data: Dictionary = {
"player_inventory": [],
"current_room_name": "",
"current_room_desc": "",
"current_room_exits": {},
"current_room_npc_array": [],
"current_room_items": [],
"side_panel_room_name": "",
"side_panel_room_desc": "",
"side_panel_room_exits": {},
"side_panel_room_npc_array": [],
"side_panel_room_items": []
}
#"current_room": Room.new()
func _save() -> void:
var file: FileAccess = FileAccess.open(FILE_PATH, FileAccess.WRITE)
file.store_var(save_data, true)
file.close()
func _load() -> void:
if FileAccess.file_exists(FILE_PATH):
var file: FileAccess = FileAccess. open(FILE_PATH, FileAccess.READ)
var data: Dictionary = file.get_var(true)
for i in data:
if save_data.has(i):
save_data[i] = data[i]
file.close()
And this is a portion of my command processor. The match statement is what commands the user has. The save and load commands is at the end of the match statement.
I included all of the functions that emit the signals.
extends Node
signal change_room(new_room)
signal update_room(cur_room)
var cur_room = null
var vertus = null
var side_panel_instance = side_panel.new()
func initialize(start_room, vertus) -> String:
self.vertus = vertus
return changing_rooms(start_room)
func process_a_command(input: String) -> String:
# Splits the string the user inputs at each space.
var player_words = input.split(" ", false)
if player_words.size() == 0:
push_error("Player has no words to parse.") #"Error: No words have been parsed."
var word1 = player_words[0].to_lower()
var word2 = ""
var word3 = ""
var word4 = ""
if player_words.size() > 1:
word2 = player_words[1].to_lower()
if player_words.size() > 2:
word3 = player_words[2].to_lower()
if player_words.size() > 3:
word4 = player_words[3].to_lower()
match word1:
"move":
var array = move(word2, word3, word4)
var s = ""
for i in array:
s = s + str(i)
return s
"help":
return player_help()
"grab":
return grab(word2, word3)
"inventory":
return show_inventory()
"drop":
return drop_item(word2, word3)
"use":
return use_item(word2, word3)
"speak":
return speak(word2)
"give":
return give(word2, word3)
"save":
# Handles saving all the information back into the player_inventory save_data.
SaveSystem.save_data.player_inventory = vertus.vertus_inventory
# Handles saving all the information back into the current room save_data.
SaveSystem.save_data.current_room_name = cur_room.room_name
SaveSystem.save_data.current_room_desc = cur_room.room_description
SaveSystem.save_data.current_room_exits = cur_room.room_exits
SaveSystem.save_data.current_room_npc_array = cur_room.npc_array
SaveSystem.save_data.current_room_items = cur_room.items
# Handles saving all the information back into the side_panel save_data.
SaveSystem.save_data.side_panel_room_name = side_panel_instance.room_name
SaveSystem.save_data.side_panel_room_desc = side_panel_instance.room_description
SaveSystem.save_data.side_panel_room_exits = side_panel_instance.exit
SaveSystem.save_data.side_panel_room_npc_array = side_panel_instance.npcs
SaveSystem.save_data.side_panel_room_items = side_panel_instance.items
SaveSystem._save()
return IType.color("Game Saved", "SYSTEM")
"load":
SaveSystem._load()
# Handles loading all the information back into the player_inventory.
vertus.vertus_inventory = SaveSystem.save_data.player_inventory
# Handles loading all the information back into the current room.
cur_room.room_name = SaveSystem.save_data.current_room_name
cur_room.room_description = SaveSystem.save_data.current_room_desc
cur_room.room_exits = SaveSystem.save_data.current_room_exits
cur_room.npc_array = SaveSystem.save_data.current_room_npc_array
cur_room.items = SaveSystem.save_data.current_room_items
# Handles loading all the information back into the side_panel.
side_panel_instance.room_name = SaveSystem.save_data.side_panel_room_name
side_panel_instance.room_description = SaveSystem.save_data.side_panel_room_desc
side_panel_instance.exit = SaveSystem.save_data.side_panel_room_exits
side_panel_instance.npcs = SaveSystem.save_data.side_panel_room_npc_array
side_panel_instance.items = SaveSystem.save_data.side_panel_room_items
return IType.color("Game Loaded", "SYSTEM")
_:
return IType.color("Think carefully...what does Vertus do?", "SYSTEM")
func grab(word2: String, word3: String) -> String:
if word2 == "":
print( "in the if")
return IType.color("There is nothing for Vertus to take.", "SYSTEM")
elif word2 != "" && word3 == "":
for items in cur_room.items:
if word2.to_lower() == items.item_name.to_lower():
cur_room.remove_items(items)
vertus.add_to_inventory(items)
emit_signal("update_room", cur_room)
return "Vertus discovers a " + IType.color(items.item_name, "ITEM") + "."
elif word2 != "" && word3 != "":
for items in cur_room.items:
var str = word2 + " " + word3
print(str)
if str == items.item_name.to_lower():
cur_room.remove_items(items)
vertus.add_to_inventory(items)
emit_signal("update_room", cur_room)
return "Vertus discovers a " + IType.color(items.item_name, "ITEM") + "."
return IType.color("Vertus searched in vain.", "SYSTEM")
func drop_item(word2: String, word3: String = "") -> String:
if word2 == "":
return IType.color("There is nothing for Vertus to drop.", "SYSTEM")
elif word2 != "" && word3 == "":
for items in vertus.vertus_inventory:
if word2.to_lower() == items.item_name.to_lower():
vertus.remove_from_inventory(items)
cur_room.add_items(items)
emit_signal("update_room", cur_room)
return "Vertus drops the " + IType.color(items.item_name, "ITEM") + "."
elif word2 != "" && word3 != "":
var str = word2 + " " + word3
print(str)
for items in vertus.vertus_inventory:
if str == items.item_name.to_lower():
vertus.remove_from_inventory(items)
cur_room.add_items(items)
emit_signal("update_room", cur_room)
return "Vertus drops the " + IType.color(items.item_name, "ITEM") + "."
return IType.color("This item is not in Vertus's posession.", "SYSTEM")
func changing_rooms(new_room: Room) -> String:
cur_room = new_room
emit_signal("change_room", new_room)
return new_room.description()
This is the game script where (handle_room_changed) and (handle_room_updated) from the side_panel.gd are used.
extends Control
@onready var command_p = $CommandParser
@onready var room_manager = $RoomManager
@onready var vertus = $Vertus
@onready var info = $GameBackground/MarginContainer/GameColumns/GameRows/GameInfo
@onready var input = $GameBackground/MarginContainer/GameColumns/GameRows/GameInput/HBoxContainer/Input
func _ready() -> void:
var side_panel = $GameBackground/MarginContainer/GameColumns/SidePanel
command_p.connect("change_room", Callable(side_panel, "handle_room_changed"))
command_p.connect("update_room", Callable(side_panel, "handle_room_updated"))
# The start up text
info.handle_response(IType.color("Welcome to Vertus's journey. Type help if ya want to see your commands.", "SYSTEM"))
var initial_room_response = command_p.initialize(room_manager.get_child(0), vertus)
info.handle_response(initial_room_response)
# Handles what happens when text is submitted.
func _on_input_text_submitted(new_text: String) -> void:
# If text is empty do nothing.
if new_text.is_empty():
return
# Otherwise process the response in the command parser.
# new_text is what the user enetered.
var response = command_p.process_a_command(new_text)
info.handle_response_w_input(response, new_text)

