224 lines
7.6 KiB
GDScript
224 lines
7.6 KiB
GDScript
class_name SaveGame extends Resource
|
|
|
|
var _is_initialised: bool = false
|
|
|
|
@export var filepath: String:
|
|
set(value):
|
|
filepath = value
|
|
if _is_initialised:
|
|
read_save_file()
|
|
changed.emit()
|
|
@export var unique_save_name: String = "frame_of_mind_%s_%s" % [Time.get_date_string_from_system(), Time.get_time_string_from_system().replace(":", "-")]:
|
|
set(value):
|
|
unique_save_name = value
|
|
if _is_initialised: changed.emit()
|
|
@export var current_room: State.rooms = State.rooms.NULL:
|
|
set(value):
|
|
current_room = value
|
|
if _is_initialised: changed.emit()
|
|
@export_flags("Intro", "Childhood", "Voice Training", "Jui Jutsu") var mementos_complete: int = 0:
|
|
set(value):
|
|
mementos_complete = value
|
|
if _is_initialised: changed.emit()
|
|
@export_flags_2d_physics var sequences_enabled: int = 63:
|
|
set(value):
|
|
sequences_enabled = value
|
|
if _is_initialised: changed.emit()
|
|
@export var board_state: Dictionary = {"cards": {}, "stickies": {}, "randoms": []}:
|
|
set(value):
|
|
board_state = value
|
|
if _is_initialised: changed.emit()
|
|
@export var childhood_mementos: Dictionary = {"cards": {}, "stickies": {}, "randoms": []}:
|
|
set(value):
|
|
childhood_mementos = value
|
|
if _is_initialised: changed.emit()
|
|
@export var is_childhood_board_complete: bool = false
|
|
@export var thumbnail: Texture = preload("res://import/interface-elements/empty_save_slot.png"):
|
|
set(value):
|
|
thumbnail = value
|
|
if _is_initialised: changed.emit()
|
|
@export var last_saved: int = int(Time.get_unix_time_from_system()):
|
|
set(value):
|
|
last_saved = value
|
|
if _is_initialised: changed.emit()
|
|
|
|
@export var is_valid: bool = false
|
|
@export var is_demo: bool = OS.has_feature("Demo")
|
|
@export var is_empty: bool = true:
|
|
get():
|
|
return not FileAccess.file_exists("%s.json:" % filepath)
|
|
@export var save_manually: bool = false:
|
|
set(val):
|
|
if val: save_to_file(thumbnail)
|
|
|
|
func _validate_property(property: Dictionary):
|
|
if property.name == filepath:
|
|
property.usage |= PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED
|
|
if property.name == "thumbnail":
|
|
property.usage |= PROPERTY_USAGE_READ_ONLY
|
|
if property.name == "is_valid":
|
|
property.usage |= PROPERTY_USAGE_READ_ONLY
|
|
if property.name == "is_empty":
|
|
property.usage |= PROPERTY_USAGE_READ_ONLY
|
|
|
|
func _init(initial_filepath = "") -> void:
|
|
if initial_filepath == "":
|
|
filepath = "%s/%s.json" % [State.user_saves_path, unique_save_name]
|
|
elif initial_filepath == "DEBUG":
|
|
filepath = initial_filepath
|
|
else:
|
|
filepath = initial_filepath
|
|
unique_save_name = initial_filepath.get_file()
|
|
read_save_file()
|
|
_is_initialised = true
|
|
|
|
if not DirAccess.dir_exists_absolute(filepath.get_base_dir()):
|
|
DirAccess.make_dir_absolute(filepath.get_base_dir())
|
|
|
|
func read_save_file():
|
|
if filepath == "DEBUG":
|
|
if OS.has_feature("debug") or OS.has_feature("demo"):
|
|
push_warning("Created DEBUG savegame. Progress will not be stored!")
|
|
else:
|
|
print(get_stack())
|
|
push_error("Created DEBUG savegame outside of demo or debug environment. This is unintentional and will lead to data loss. Please contact support and attatch the stack above.")
|
|
#TODO maybe cause a crash here?
|
|
return
|
|
|
|
if FileAccess.file_exists(filepath):
|
|
print("Opening existing Savegame: %s" % filepath)
|
|
var file := FileAccess.open(filepath, FileAccess.READ)
|
|
var raw_json := FileAccess.get_file_as_string(filepath)
|
|
file.close()
|
|
var parsed: Dictionary = JSON.parse_string(raw_json)
|
|
|
|
var tmp_img: Image
|
|
|
|
if FileAccess.file_exists("%s/thumbnails/%s.png" % [filepath.get_base_dir(), unique_save_name]):
|
|
tmp_img = Image.load_from_file("%s/thumbnails/%s.png" % [filepath.get_base_dir(), unique_save_name])
|
|
|
|
var are_types_valid := (
|
|
parsed["unique_save_name"] is String and
|
|
parsed["current_room"] is float and
|
|
parsed["mementos_complete"] is float and
|
|
parsed["board_state"] is Dictionary and
|
|
parsed["is_childhood_board_complete"] is bool and
|
|
parsed["last_saved"] is float# and FIXME
|
|
#parsed["demo"] is bool and last_saved != 0
|
|
)
|
|
|
|
if are_types_valid:
|
|
for key in parsed.keys():
|
|
set(key, parsed[key])
|
|
|
|
for dict:Dictionary in [board_state["cards"], board_state["stickies"]]:
|
|
for key in dict.keys():
|
|
if dict[key] is String:
|
|
if dict[key].begins_with("("):
|
|
dict[key] = parse_vec_from_string(dict[key])
|
|
|
|
var cards: Dictionary[StringName, Variant]
|
|
var stickies: Dictionary[StringName, Variant]
|
|
var randoms: Array[StringName]
|
|
|
|
for cardname:String in board_state["cards"]:
|
|
cards[StringName(cardname)] = board_state["cards"][cardname]
|
|
for sticky_name:String in board_state["stickies"]:
|
|
stickies[StringName(sticky_name)] = board_state["stickies"][sticky_name]
|
|
for random_name:StringName in board_state["randoms"]:
|
|
randoms.append( random_name )
|
|
|
|
board_state = {
|
|
"cards": cards,
|
|
"stickies": stickies,
|
|
"randoms": randoms
|
|
}
|
|
|
|
is_valid = are_types_valid \
|
|
and current_room >= 0 \
|
|
and current_room < State.rooms.keys().size() \
|
|
and validate_board_state()
|
|
|
|
if not is_valid:
|
|
push_error("Parsing of Save failed.")
|
|
|
|
if tmp_img != null:
|
|
thumbnail = ImageTexture.create_from_image(tmp_img)
|
|
is_empty = false
|
|
else:
|
|
print("Creating empty Savegame: %s" % filepath)
|
|
is_valid = true
|
|
|
|
func _get_save_dict() -> Dictionary:
|
|
return {
|
|
"unique_save_name": unique_save_name,
|
|
"current_room": current_room,
|
|
"mementos_complete": mementos_complete,
|
|
"sequences_enabled": sequences_enabled,
|
|
"childhood_mementos": childhood_mementos,
|
|
"board_state": board_state,
|
|
"is_childhood_board_complete": is_childhood_board_complete,
|
|
"last_saved": last_saved,
|
|
"is_demo": is_demo
|
|
}
|
|
|
|
func save_to_file(screen_shot: Texture) -> void:
|
|
if filepath == "DEBUG":
|
|
push_warning("Saving DEBUG save skipped. This is intentional.")
|
|
return
|
|
|
|
if current_room == State.rooms.NULL:
|
|
print("Not saving empty savegame.")
|
|
return
|
|
|
|
last_saved = int(Time.get_unix_time_from_system())
|
|
var thumbnail_image: Image = screen_shot.get_image()
|
|
thumbnail_image.convert(Image.Format.FORMAT_RGB8)
|
|
thumbnail_image.linear_to_srgb()
|
|
thumbnail_image.resize(384, 216, Image.INTERPOLATE_LANCZOS) # nonexistent call in ViewportTexture
|
|
thumbnail_image.crop(384, 216)
|
|
|
|
var save_dir := DirAccess.open(filepath.get_base_dir())
|
|
if not save_dir.dir_exists("thumbnails"):
|
|
save_dir.make_dir("thumbnails")
|
|
|
|
var thumbnail_path: String = "%s/thumbnails/%s.png" % [filepath.get_base_dir(), unique_save_name]
|
|
thumbnail_image.save_png(thumbnail_path)
|
|
print(filepath.get_base_dir())
|
|
var file := FileAccess.open(filepath, FileAccess.WRITE)
|
|
file.store_string(JSON.stringify(_get_save_dict()))
|
|
file.close()
|
|
|
|
|
|
func calculate_completed_sequences() -> int:
|
|
var i: int = mementos_complete - ((mementos_complete >> 1) & 0x55555555);
|
|
i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
|
|
i = (i + (i >> 4)) & 0x0F0F0F0F;
|
|
i *= 0x01010101;
|
|
return i >> 24;
|
|
|
|
func calculate_total_connections() -> int:
|
|
var connections:= 0
|
|
|
|
for sticky_position in board_state.stickies.values():
|
|
connections += int(sticky_position is String)
|
|
|
|
return connections
|
|
|
|
func validate_board_state() -> bool:
|
|
if board_state.keys().has("cards") and board_state.keys().has("stickies"):
|
|
for card in board_state.cards.values():
|
|
if not card is Vector2:
|
|
push_error("Save %s could not be parsed: Corrupted Cards." % unique_save_name)
|
|
return false
|
|
for sticky in board_state.stickies.values():
|
|
if not (sticky is int or sticky is Vector2 or board_state.cards.keys().has(sticky)):
|
|
push_error("Save %s could not be parsed: Corrupted Sticky Notes.")
|
|
return false
|
|
return true
|
|
return false
|
|
|
|
func parse_vec_from_string(string: String) -> Vector2:
|
|
var string_array := string.replace("(", "").replace(")", "").split(", ")
|
|
return Vector2(float(string_array[0]), float(string_array[1]))
|