2024-10-01 23:26:54 +00:00
class_name SaveGame extends Resource
2025-03-25 21:34:13 +00:00
var _is_initialised : bool = false
2024-10-02 23:10:35 +00:00
2025-03-25 21:34:13 +00:00
@ 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 ( )
2025-10-29 21:49:29 +00:00
@ export_flags_2d_physics var sequences_enabled : int = 63 :
set ( value ) :
sequences_enabled = value
if _is_initialised : changed . emit ( )
2025-06-03 21:18:34 +00:00
@ export var board_state : Dictionary = { " cards " : { } , " stickies " : { } , " randoms " : [ ] } :
2025-03-25 21:34:13 +00:00
set ( value ) :
board_state = value
if _is_initialised : changed . emit ( )
2025-10-29 21:49:29 +00:00
@ export var childhood_mementos : Dictionary = { " cards " : { } , " stickies " : { } , " randoms " : [ ] } :
set ( value ) :
childhood_mementos = value
if _is_initialised : changed . emit ( )
2025-06-03 21:18:34 +00:00
@ export var is_childhood_board_complete : bool = false
2025-03-25 21:34:13 +00:00
@ 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 ( )
2024-10-02 23:10:35 +00:00
2025-03-25 21:34:13 +00:00
@ export var is_valid : bool = false
2025-05-26 17:43:26 +00:00
@ export var is_demo : bool = OS . has_feature ( " Demo " )
2025-03-25 21:34:13 +00:00
@ 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 )
2025-12-08 09:43:02 +00:00
2024-10-06 09:38:15 +00:00
func _validate_property ( property : Dictionary ) :
2025-03-25 21:34:13 +00:00
if property . name == filepath :
property . usage |= PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED
2024-10-06 09:38:15 +00:00
if property . name == " thumbnail " :
2025-03-25 21:34:13 +00:00
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
2024-10-06 09:38:15 +00:00
2025-03-23 13:56:09 +00:00
func _init ( initial_filepath = " " ) - > void :
if initial_filepath == " " :
2025-05-26 17:43:26 +00:00
filepath = " %s / %s .json " % [ State . user_saves_path , unique_save_name ]
2025-09-04 23:54:21 +00:00
elif initial_filepath == " DEBUG " :
filepath = initial_filepath
2025-03-23 13:56:09 +00:00
else :
filepath = initial_filepath
2025-03-25 21:34:13 +00:00
unique_save_name = initial_filepath . get_file ( )
2024-10-02 23:10:35 +00:00
read_save_file ( )
2025-03-25 21:34:13 +00:00
_is_initialised = true
2025-12-08 09:43:02 +00:00
2025-05-26 17:43:26 +00:00
if not DirAccess . dir_exists_absolute ( filepath . get_base_dir ( ) ) :
DirAccess . make_dir_absolute ( filepath . get_base_dir ( ) )
2024-10-02 23:10:35 +00:00
func read_save_file ( ) :
2025-09-04 23:54:21 +00:00
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
2025-03-25 21:34:13 +00:00
2025-05-26 17:43:26 +00:00
if FileAccess . file_exists ( filepath ) :
2025-10-29 21:49:29 +00:00
print ( " Opening existing Savegame: %s " % filepath )
2025-12-12 18:19:37 +00:00
var file : = FileAccess . open ( filepath , FileAccess . READ )
var raw_json : = FileAccess . get_file_as_string ( filepath )
2024-10-02 23:10:35 +00:00
file . close ( )
var parsed : Dictionary = JSON . parse_string ( raw_json )
2025-12-08 09:43:02 +00:00
2024-10-07 09:23:19 +00:00
var tmp_img : Image
2025-12-08 09:43:02 +00:00
2025-03-25 21:34:13 +00:00
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 ] )
2025-12-08 09:43:02 +00:00
2025-12-12 18:19:37 +00:00
var are_types_valid : = (
2025-02-24 15:06:21 +00:00
parsed [ " unique_save_name " ] is String and
2025-03-25 21:34:13 +00:00
parsed [ " current_room " ] is float and
parsed [ " mementos_complete " ] is float and
parsed [ " board_state " ] is Dictionary and
2025-06-03 21:18:34 +00:00
parsed [ " is_childhood_board_complete " ] is bool and
2025-10-29 21:49:29 +00:00
parsed [ " last_saved " ] is float # and FIXME
#parsed["demo"] is bool and last_saved != 0
2024-10-02 23:10:35 +00:00
)
2025-12-08 09:43:02 +00:00
2025-03-25 21:34:13 +00:00
if are_types_valid :
2024-10-02 23:10:35 +00:00
for key in parsed . keys ( ) :
set ( key , parsed [ key ] )
2025-12-08 09:43:02 +00:00
2025-10-29 21:49:29 +00:00
for dict : Dictionary in [ board_state [ " cards " ] , board_state [ " stickies " ] ] :
2025-03-25 21:34:13 +00:00
for key in dict . keys ( ) :
if dict [ key ] is String :
if dict [ key ] . begins_with ( " ( " ) :
dict [ key ] = parse_vec_from_string ( dict [ key ] )
2025-12-08 09:43:02 +00:00
2025-05-16 21:50:19 +00:00
var cards : Dictionary [ StringName , Variant ]
var stickies : Dictionary [ StringName , Variant ]
2025-06-03 21:18:34 +00:00
var randoms : Array [ StringName ]
2025-12-08 09:43:02 +00:00
2025-05-16 21:50:19 +00:00
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 ]
2025-06-03 21:18:34 +00:00
for random_name : StringName in board_state [ " randoms " ] :
2025-10-29 21:49:29 +00:00
randoms . append ( random_name )
2025-12-08 09:43:02 +00:00
2025-05-16 21:50:19 +00:00
board_state = {
" cards " : cards ,
2025-06-03 21:18:34 +00:00
" stickies " : stickies ,
" randoms " : randoms
2025-05-16 21:50:19 +00:00
}
2025-12-08 09:43:02 +00:00
2025-03-25 21:34:13 +00:00
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. " )
2025-12-08 09:43:02 +00:00
2024-10-07 09:23:19 +00:00
if tmp_img != null :
thumbnail = ImageTexture . create_from_image ( tmp_img )
2025-03-25 21:34:13 +00:00
is_empty = false
else :
2025-10-29 21:49:29 +00:00
print ( " Creating empty Savegame: %s " % filepath )
2025-03-25 21:34:13 +00:00
is_valid = true
2024-10-06 09:38:15 +00:00
func _get_save_dict ( ) - > Dictionary :
return {
" unique_save_name " : unique_save_name ,
" current_room " : current_room ,
" mementos_complete " : mementos_complete ,
2025-10-29 21:33:11 +00:00
" sequences_enabled " : sequences_enabled ,
" childhood_mementos " : childhood_mementos ,
2024-10-06 09:38:15 +00:00
" board_state " : board_state ,
2025-06-03 21:18:34 +00:00
" is_childhood_board_complete " : is_childhood_board_complete ,
2025-05-26 17:43:26 +00:00
" last_saved " : last_saved ,
" is_demo " : is_demo
2024-10-06 09:38:15 +00:00
}
2025-12-12 18:19:37 +00:00
func save_to_file ( screen_shot : Texture ) - > void :
2025-09-04 23:54:21 +00:00
if filepath == " DEBUG " :
push_warning ( " Saving DEBUG save skipped. This is intentional. " )
return
2025-12-08 09:43:02 +00:00
2025-10-29 21:49:29 +00:00
if current_room == State . rooms . NULL :
print ( " Not saving empty savegame. " )
return
2025-12-08 09:43:02 +00:00
2025-12-12 18:19:37 +00:00
last_saved = int ( Time . get_unix_time_from_system ( ) )
var thumbnail_image : Image = screen_shot . get_image ( )
2025-05-26 17:43:26 +00:00
thumbnail_image . convert ( Image . Format . FORMAT_RGB8 )
thumbnail_image . linear_to_srgb ( )
2025-12-12 18:19:37 +00:00
thumbnail_image . resize ( 384 , 216 , Image . INTERPOLATE_LANCZOS ) # nonexistent call in ViewportTexture
thumbnail_image . crop ( 384 , 216 )
2025-12-08 09:43:02 +00:00
2025-12-12 18:19:37 +00:00
var save_dir : = DirAccess . open ( filepath . get_base_dir ( ) )
2025-03-25 21:34:13 +00:00
if not save_dir . dir_exists ( " thumbnails " ) :
save_dir . make_dir ( " thumbnails " )
2025-12-08 09:43:02 +00:00
2025-12-12 18:19:37 +00:00
var thumbnail_path : String = " %s /thumbnails/ %s .png " % [ filepath . get_base_dir ( ) , unique_save_name ]
thumbnail_image . save_png ( thumbnail_path )
2025-03-25 21:34:13 +00:00
print ( filepath . get_base_dir ( ) )
2025-12-12 18:19:37 +00:00
var file : = FileAccess . open ( filepath , FileAccess . WRITE )
2024-10-02 23:10:35 +00:00
file . store_string ( JSON . stringify ( _get_save_dict ( ) ) )
2025-03-25 21:34:13 +00:00
file . close ( )
2025-12-08 09:43:02 +00:00
2025-03-25 21:34:13 +00:00
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
2025-12-08 09:43:02 +00:00
2025-03-25 21:34:13 +00:00
for sticky_position in board_state . stickies . values ( ) :
connections += int ( sticky_position is String )
2025-12-08 09:43:02 +00:00
2025-03-25 21:34:13 +00:00
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 ( ) :
2025-12-02 20:32:30 +00:00
if not ( sticky is int or sticky is Vector2 or board_state . cards . keys ( ) . has ( sticky ) ) :
2025-03-25 21:34:13 +00:00
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 :
2025-12-12 18:19:37 +00:00
var string_array : = string . replace ( " ( " , " " ) . replace ( " ) " , " " ) . split ( " , " )
2025-03-25 21:34:13 +00:00
return Vector2 ( float ( string_array [ 0 ] ) , float ( string_array [ 1 ] ) )