diff --git a/src/Autoload/GlobalState.gd b/src/Autoload/GlobalState.gd index 6febed0..68f1256 100644 --- a/src/Autoload/GlobalState.gd +++ b/src/Autoload/GlobalState.gd @@ -41,87 +41,109 @@ func get_progress() -> Dictionary: func save() -> void: SaveManager.save_default() - -func get_level_completed(levelName: String) -> bool: - if gsr.progress_dict.has(levelName) && gsr.progress_dict[levelName].has("levelcompleted"): - return gsr.progress_dict[levelName]["levelcompleted"] +# Returns if the level was completed or not +func get_level_completed(level_name: String) -> bool: + if gsr.progress_dict.has(level_name) && gsr.progress_dict[level_name].has("levelcompleted"): + return gsr.progress_dict[level_name]["levelcompleted"] else: return false -func set_level_completed(levelName: String, state: bool) -> void: - if !gsr.progress_dict.has(levelName): - gsr.progress_dict[levelName] = {} - gsr.progress_dict[levelName]["levelcompleted"] = state +func set_level_completed(level_name: String, state: bool) -> void: + if !gsr.progress_dict.has(level_name): + gsr.progress_dict[level_name] = {} + gsr.progress_dict[level_name]["levelcompleted"] = state SaveManager.save_default() -func set_leveltime(levelName: String, time: float) -> void: - if !gsr.progress_dict.has(levelName): - gsr.progress_dict[levelName] = {} - gsr.progress_dict[levelName]["leveltime"] = time +func set_leveltime(level_name: String, time: float) -> void: + if !gsr.progress_dict.has(level_name): + gsr.progress_dict[level_name] = {} + gsr.progress_dict[level_name]["leveltime"] = time SaveManager.save_default() -func get_level_time(levelName: String) -> float: - if gsr.progress_dict.has(levelName) && gsr.progress_dict[levelName].has("leveltime"): - return gsr.progress_dict[levelName]["leveltime"] - else: - return INF -func set_uncompleted_level_time(levelName: String, time: float) -> void: - if !gsr.progress_dict.has(levelName): - gsr.progress_dict[levelName] = {} - gsr.progress_dict[levelName]["uncompletedleveltime"] = time - SaveManager.save_default() - -func get_uncompleted_level_time(levelName: String) -> float: - if gsr.progress_dict.has(levelName) && gsr.progress_dict[levelName].has("uncompletedleveltime"): - return gsr.progress_dict[levelName]["uncompletedleveltime"] +func get_level_time(level_name: String) -> float: + if gsr.progress_dict.has(level_name) && gsr.progress_dict[level_name].has("leveltime"): + return gsr.progress_dict[level_name]["leveltime"] else: return INF -func set_savepoint(levelName: String, position: Vector2) -> void: +func set_uncompleted_level_time(level_name: String, time: float) -> void: + if !gsr.progress_dict.has(level_name): + gsr.progress_dict[level_name] = {} + gsr.progress_dict[level_name]["uncompletedleveltime"] = time + SaveManager.save_default() + + +func get_uncompleted_level_time(level_name: String) -> float: + if gsr.progress_dict.has(level_name) && gsr.progress_dict[level_name].has("uncompletedleveltime"): + return gsr.progress_dict[level_name]["uncompletedleveltime"] + else: + return INF + + +func set_savepoint(level_name: String, position: Vector2) -> void: #TODO You can free a frog, go to the checkpoint and it will be # saved as freed forever - if !gsr.progress_dict.has(levelName): - gsr.progress_dict[levelName] = {} - gsr.progress_dict[levelName]["savepoint"] = position + if !gsr.progress_dict.has(level_name): + gsr.progress_dict[level_name] = {} + gsr.progress_dict[level_name]["savepoint"] = position SaveManager.save_default() -func remove_savepoint(levelName: String) -> void: - if !gsr.progress_dict.has(levelName): +func set_level_state(level_name: String, property_dict: Dictionary) -> void: + if !gsr.progress_dict.has(level_name): + gsr.progress_dict[level_name] = {} + gsr.progress_dict[level_name]["savestate"] = property_dict + SaveManager.save_default() + + +func remove_savepoint(level_name: String) -> void: + if !gsr.progress_dict.has(level_name): return - gsr.progress_dict[levelName].erase("savepoint") - gsr.progress_dict[levelName].erase("uncompletedleveltime") + gsr.progress_dict[level_name].erase("savepoint") + gsr.progress_dict[level_name].erase("uncompletedleveltime") + SaveManager.save_default() + +func remove_savestate(level_name: String) -> void: + if !gsr.progress_dict.has(level_name): + return + gsr.progress_dict[level_name].erase("savestate") SaveManager.save_default() -func get_property_value(levelName: String, propertyName: String) -> int: - if gsr.progress_dict.has(levelName) && gsr.progress_dict[levelName].has(propertyName): - return gsr.progress_dict[levelName][propertyName] +func get_property_value(level_name: String, propertyName: String) -> int: + if gsr.progress_dict.has(level_name) && gsr.progress_dict[level_name].has(propertyName): + return gsr.progress_dict[level_name][propertyName] else: return 0 - -func get_savepoint(levelName: String) -> Vector2: - if gsr.progress_dict.has(levelName) && gsr.progress_dict[levelName].has("savepoint"): - return gsr.progress_dict[levelName]["savepoint"] +func get_savepoint(level_name: String) -> Vector2: + if gsr.progress_dict.has(level_name) && gsr.progress_dict[level_name].has("savepoint"): + return gsr.progress_dict[level_name]["savepoint"] else: return Vector2() -func was_level_touched(levelName: String) -> bool: +func get_savestate(level_name: String) -> Dictionary: + if gsr.progress_dict.has(level_name) && gsr.progress_dict[level_name].has("savestate"): + return gsr.progress_dict[level_name]["savestate"] + else: + return {} + + +func was_level_touched(level_name: String) -> bool: if OS.is_debug_build(): return true - if !gsr.progress_dict.has(levelName) || !gsr.progress_dict[levelName].has("touched"): + if !gsr.progress_dict.has(level_name) || !gsr.progress_dict[level_name].has("touched"): return false - return gsr.progress_dict[levelName]["touched"] + return gsr.progress_dict[level_name]["touched"] -func touch_level(levelName: String) -> void: - if !gsr.progress_dict.has(levelName): - gsr.progress_dict[levelName] = {} - gsr.progress_dict[levelName]["touched"] = true +func touch_level(level_name: String) -> void: + if !gsr.progress_dict.has(level_name): + gsr.progress_dict[level_name] = {} + gsr.progress_dict[level_name]["touched"] = true # TODO This is permanent immediatly diff --git a/src/BenefitialObjects/Coin.gd b/src/BenefitialObjects/Coin.gd index 81cc337..423e34d 100644 --- a/src/BenefitialObjects/Coin.gd +++ b/src/BenefitialObjects/Coin.gd @@ -1,9 +1,22 @@ extends Area2D onready var anim_player: AnimationPlayer = get_node("AnimationPlayer") -onready var level_state := $"%LevelState" +onready var level_state := get_tree().root.get_child(4).get_node("%LevelState") -export var currencyValue: = 1 +export onready var was_collected := false +export var currencyValue := 1 + +var scene_saved_id := 0 + +func _ready() -> void: + scene_saved_id = level_state.register_saveable_object(self) + var collected_saved = level_state.get_saved_object_property(scene_saved_id, "was_collected") + + if collected_saved != null: + was_collected = collected_saved + + if was_collected: + collected() func _on_body_entered(_body: Node) -> void: if $AudioStreamPlayer.playing: diff --git a/src/BenefitialObjects/SavePoint.gd b/src/BenefitialObjects/SavePoint.gd index 3632b84..4af42ee 100644 --- a/src/BenefitialObjects/SavePoint.gd +++ b/src/BenefitialObjects/SavePoint.gd @@ -3,7 +3,7 @@ extends Node2D onready var level_state := get_tree().root.get_child(4).get_node("%LevelState") func _ready() -> void: - if(GlobalState.get_savepoint(level_state.levelName) == global_position + Vector2(0,18)): + if(GlobalState.get_savepoint(level_state.level_name) == global_position + Vector2(0,18)): $Flag.material.set_shader_param("speed", 0.6) $Flag.material.set_shader_param("amplitude", 1) $Flag.material.set_shader_param("inclination", 1) @@ -12,7 +12,7 @@ func _ready() -> void: #TODO What should be saved when reaching a savepoint besides the position in the level func _on_SaveArea_area_entered(area: Area2D) -> void: #TODO Spawnheight fixed - if(!GlobalState.get_savepoint(level_state.levelName) == global_position + Vector2(0,18)): + if(!GlobalState.get_savepoint(level_state.level_name) == global_position + Vector2(0,18)): $AnimationPlayer.play("rolloutflag") $AudioStreamPlayer.play() level_state.set_savepoint(global_position + Vector2(0,18)) diff --git a/src/Contraptions/Portal/Portal.gd b/src/Contraptions/Portal/Portal.gd index 1421752..4ab93ee 100644 --- a/src/Contraptions/Portal/Portal.gd +++ b/src/Contraptions/Portal/Portal.gd @@ -4,7 +4,6 @@ extends Area2D onready var anim_player: AnimationPlayer = $AnimationPlayer onready var level_state := get_tree().root.get_child(4).get_node("%LevelState") onready var signal_manager := get_tree().root.get_child(4).get_node("%SignalManager") -onready var levelName := get_tree().get_current_scene().get_name() export(String, FILE, "*.tscn") var next_scene export(bool) var is_active diff --git a/src/Contraptions/Triggers/FrogFreeButton.gd b/src/Contraptions/Triggers/FrogFreeButton.gd index f184433..f90b353 100644 --- a/src/Contraptions/Triggers/FrogFreeButton.gd +++ b/src/Contraptions/Triggers/FrogFreeButton.gd @@ -2,16 +2,27 @@ extends Node2D signal button_pushed +onready var level_state := get_tree().root.get_child(4).get_node("%LevelState") onready var activatorArea = $"%ActivatorArea" onready var indicatorPlayer = $"%IndicatorPlayer" onready var signal_manager := get_tree().root.get_child(4).get_node("%SignalManager") onready var unactivatable_timer := $Timer export(int) var frog_number := 0 +onready var activated := false + +var scene_saved_id := 0 var activatable = true func _ready() -> void: + scene_saved_id = level_state.register_saveable_object(self) + var activated_saved = level_state.get_saved_object_property(scene_saved_id, "activated") + + if activated_saved != null: + activated = activated_saved $Digit.frame = frog_number + if activated: + selfActivate() func selfActivate(): diff --git a/src/Contraptions/Triggers/GateButton.gd b/src/Contraptions/Triggers/GateButton.gd index f75b750..b549e2d 100644 --- a/src/Contraptions/Triggers/GateButton.gd +++ b/src/Contraptions/Triggers/GateButton.gd @@ -3,18 +3,38 @@ extends Node2D onready var activatorArea = $"%ActivatorArea" onready var indicatorPlayer = $"%IndicatorPlayer" onready var signal_manager := get_tree().root.get_child(4).get_node("%SignalManager") +onready var level_state := get_tree().root.get_child(4).get_node("%LevelState") onready var unactivatable_timer := $Timer -var activatable = true - +export onready var activated := false -func selfActivate(): +var scene_saved_id := 0 +var activatable := true + +func _ready() -> void: + scene_saved_id = level_state.register_saveable_object(self) + var activated_saved = level_state.get_saved_object_property(scene_saved_id, "activated") + + if activated_saved != null: + activated = activated_saved + + signal_manager.connect("unlocked", self, "receive_unlock") + if activated: + selfActivate() + +func selfActivate() -> void: indicatorPlayer.play("onning") #TODO dis importante activatorArea.set_deferred("monitoring", false) #TODO Close gate again? signal_manager.emit_signal("unlocked", "gateblock") + activated = true +func receive_unlock(event: String) -> void: + # TODO For god sake make the events an enum? #debatable #polymorphism + if event == "gateblock": + indicatorPlayer.play("onning") + activated = true func _on_ActivatorArea_body_entered(body: Node) -> void: if(!body.is_in_group("player") && !body.is_in_group("frog")): diff --git a/src/Contraptions/VendingMachine.gd b/src/Contraptions/VendingMachine.gd index 2ab4830..e829eb7 100644 --- a/src/Contraptions/VendingMachine.gd +++ b/src/Contraptions/VendingMachine.gd @@ -5,12 +5,23 @@ onready var signal_manager := get_tree().root.get_child(4).get_node("%SignalMana onready var level_state := get_tree().root.get_child(4).get_node("%LevelState") onready var blobby := $"../%Blobby" onready var unactivatable_timer := $Timer + +export onready var activated := false export var cost := 3 +var scene_saved_id := 0 var activatable = false func _ready(): + scene_saved_id = level_state.register_saveable_object(self) + var activated_saved = level_state.get_saved_object_property(scene_saved_id, "activated") + + if activated_saved != null: + activated = activated_saved + $Sprite.frame = 0 + if activated: + selfActivate() func _process(delta): # TODO Global currency count?? Maybe just level intern currency @@ -26,6 +37,7 @@ func selfActivate(): $AudioStreamPlayer.play() #TODO dis importante activatorArea.set_deferred("monitoring", false) + $Sprite.frame = 1 func _on_ActivatorArea_area_entered(area:Area2D) -> void: @@ -42,4 +54,3 @@ func _on_Timer_timeout(): $Highlight.visible = false $Label.visible = false activatable = false - $Sprite.frame = 1 diff --git a/src/ObstacleObjects/Mine.gd b/src/ObstacleObjects/Mine.gd index dab96a0..c7154bc 100644 --- a/src/ObstacleObjects/Mine.gd +++ b/src/ObstacleObjects/Mine.gd @@ -1,15 +1,22 @@ extends Node2D -# Declare member variables here. Examples: -# var a: int = 2 -# var b: String = "text" -export var is_armed = false +onready var level_state := get_tree().root.get_child(4).get_node("%LevelState") + +export onready var is_armed = false var trigger_zone_entered: bool = false +var scene_saved_id := 0 +var activatable = false + +func _ready(): + scene_saved_id = level_state.register_saveable_object(self) + var is_armed_saved = level_state.get_saved_object_property(scene_saved_id, "is_armed") + + if is_armed_saved != null: + is_armed = is_armed_saved -func _ready() -> void: if(!is_armed): if($HarmfulArea.is_in_group("harmful")): $HarmfulArea.remove_from_group("harmful") diff --git a/src/StateMachines/BlobbyStateMachine.gd b/src/StateMachines/BlobbyStateMachine.gd index 105ddcb..b213430 100644 --- a/src/StateMachines/BlobbyStateMachine.gd +++ b/src/StateMachines/BlobbyStateMachine.gd @@ -47,7 +47,7 @@ func _ready(): # Zero Vector is false if level_state.load_savepoint(): - parent.global_position = GlobalState.get_savepoint(level_state.levelName) + parent.global_position = GlobalState.get_savepoint(level_state.level_name) # Calls the parent behaviours according to state diff --git a/src/UserInterface/Buttons/RetryCompleteButton.gd b/src/UserInterface/Buttons/RetryCompleteButton.gd index e252646..38e2567 100644 --- a/src/UserInterface/Buttons/RetryCompleteButton.gd +++ b/src/UserInterface/Buttons/RetryCompleteButton.gd @@ -3,6 +3,7 @@ extends AudibleButton onready var level_state := get_tree().root.get_child(4).get_node("%LevelState") func _on_button_up() -> void: - GlobalState.remove_savepoint(level_state.levelName) + GlobalState.remove_savepoint(level_state.level_name) + GlobalState.remove_savestate(level_state.level_name) get_tree().paused = false get_tree().reload_current_scene() diff --git a/src/UserInterface/Screens/MainMenu/MainScreen.gd b/src/UserInterface/Screens/MainMenu/MainScreen.gd index 4f10008..43a5c0c 100644 --- a/src/UserInterface/Screens/MainMenu/MainScreen.gd +++ b/src/UserInterface/Screens/MainMenu/MainScreen.gd @@ -2,5 +2,6 @@ extends Control func _ready() -> void: - $"%PlayButton".grab_focus() - GlobalAudio.play_scene_independent("res://assets/music/Shopping For The Future (LOOP).wav","Music", -17, true) + get_tree().paused = false + $"%PlayButton".grab_focus() + GlobalAudio.play_scene_independent("res://assets/music/Shopping For The Future (LOOP).wav","Music", -17, true) diff --git a/src/Utilities/LevelState.gd b/src/Utilities/LevelState.gd index cf63c8c..413ed9f 100644 --- a/src/Utilities/LevelState.gd +++ b/src/Utilities/LevelState.gd @@ -1,7 +1,7 @@ extends Node onready var signal_manager := get_tree().root.get_child(4).get_node("%SignalManager") -onready var levelName := get_tree().current_scene.filename +onready var level_name := get_tree().current_scene.filename #TODO Easteregg pls var currency := 0 setget set_currency, get_currency @@ -12,13 +12,18 @@ var is_dead := false setget set_dead var level_time := 0.0 +var saved_property_dictionary := {} +var saved_object_dictionary := {} +var object_in_scene_id: int = 0 + + func _ready() -> void: - GlobalState.touch_level(levelName) - GlobalState.gsr.last_played_level = levelName + GlobalState.touch_level(level_name) + GlobalState.gsr.last_played_level = level_name SaveManager.save_default() signal_manager.connect("level_completed", self, "_on_level_completed") signal_manager.connect("player_died", self, "player_dying") - + func _physics_process(delta: float) -> void: level_time += delta @@ -29,8 +34,8 @@ func reset() -> void: currency = 0 freed_frogs = [] # TODO Maybe not the place for this? - if GlobalState.gsr.progress_dict.has(levelName): - GlobalState.gsr.progress_dict[levelName].erase("savepoint") + if GlobalState.gsr.progress_dict.has(level_name): + GlobalState.gsr.progress_dict[level_name].erase("savepoint") func set_currency(value: int) -> void: @@ -46,21 +51,58 @@ func set_deaths(value: int) -> void: func set_dead(value: bool) -> void: is_dead = value +func get_own_scene_id(obj: Object) -> int: + if "scene_id" in obj && saved_object_dictionary.has(obj.scene_id): + return obj.scene_id + else: + return register_saveable_object(obj) + + +func register_saveable_object(obj: Object) -> int: + var id = object_in_scene_id + saved_object_dictionary[id] = obj + object_in_scene_id += 1 + return id + + +func save_object_properties() -> void: + for id in saved_object_dictionary.keys(): + var object = saved_object_dictionary[id] + var property_list = object.get_property_list() + var saved_properties = {} + for property in property_list: + # Only script Variables and only "primitive" types + if property["usage"] == PROPERTY_USAGE_SCRIPT_VARIABLE && property["type"] <= 16: + saved_properties[property["name"]] = object.get(property["name"]) + saved_property_dictionary[id] = saved_properties + +func get_saved_object_property(id: int, property: String): + if saved_property_dictionary.has(id) && saved_property_dictionary[id].has(property): + return saved_property_dictionary[id][property] + else: + return null + + func set_savepoint(pos: Vector2) -> void: - GlobalState.set_savepoint(levelName, pos) - GlobalState.set_uncompleted_level_time(levelName, level_time) + GlobalState.set_savepoint(level_name, pos) + save_object_properties() + GlobalState.set_uncompleted_level_time(level_name, level_time) + GlobalState.set_level_state(level_name, saved_property_dictionary) + func load_savepoint() -> bool: - if !GlobalState.get_savepoint(levelName): + if !GlobalState.get_savepoint(level_name): return false - level_time = GlobalState.get_uncompleted_level_time(levelName) + saved_property_dictionary = GlobalState.get_savestate(level_name) + level_time = GlobalState.get_uncompleted_level_time(level_name) return true + # Registers a new frog which exists in the loaded level, with the progress resource func register_frog(number: int, freed: bool = false) -> void: update_global_state() - if !GlobalState.gsr.progress_dict[levelName]["froggies"].has(number): - GlobalState.gsr.progress_dict[levelName]["froggies"][number] = freed + if !GlobalState.gsr.progress_dict[level_name]["froggies"].has(number): + GlobalState.gsr.progress_dict[level_name]["froggies"][number] = freed GlobalState.save() @@ -96,13 +138,8 @@ func spend_currency(cost: int) -> bool: return true if check_balance() < cost: return false - var remainder = currency - cost - if remainder >= 0: - set_currency(remainder) - # When level collected currency is not enough, deplete saved up currency in global state - else: - currency = 0 - GlobalState.set_wallet(GlobalState.gsr.wallet + remainder) + # Can get negative if the cost is greater than what was collected in the same level + currency = currency - cost return true func check_balance() -> int: @@ -113,15 +150,17 @@ func _on_level_completed(): #if(OS.is_debug_build()): # return # TODO Extra screen for new best time - GlobalState.set_level_completed(levelName, true) - if(GlobalState.get_level_time(levelName) > level_time ): - GlobalState.set_leveltime(levelName, level_time) - GlobalState.set_uncompleted_level_time(levelName, INF) - GlobalState.remove_savepoint(levelName) + GlobalState.set_level_completed(level_name, true) + if(GlobalState.get_level_time(level_name) > level_time ): + GlobalState.set_leveltime(level_name, level_time) + GlobalState.set_uncompleted_level_time(level_name, INF) + GlobalState.remove_savepoint(level_name) + GlobalState.remove_savestate(level_name) update_global_state() reset() - +# TODO This is now inconsistent as the level_completed property could be used to +# determine what was achieved for good and what progress was lost due to death/reloading func update_global_state() -> void: var progress_dict: Dictionary = GlobalState.get_progress() var levelProgress: Dictionary = {} @@ -130,29 +169,28 @@ func update_global_state() -> void: levelProgress["deaths"] = deaths # TODO Doesnt account for multiple plays of same level - if !progress_dict.has(levelName): - progress_dict[levelName] = levelProgress + if !progress_dict.has(level_name): + progress_dict[level_name] = levelProgress else: - progress_dict[levelName]["currency"] = ( - GlobalState.get_property_value(levelName, "currency") + progress_dict[level_name]["currency"] = ( + GlobalState.get_property_value(level_name, "currency") + currency ) - progress_dict[levelName]["deaths"] = ( - GlobalState.get_property_value(levelName, "deaths") + progress_dict[level_name]["deaths"] = ( + GlobalState.get_property_value(level_name, "deaths") + deaths ) - if !progress_dict[levelName].has("froggies"): - progress_dict[levelName]["froggies"] = {} - else: + if !progress_dict[level_name].has("froggies"): + progress_dict[level_name]["froggies"] = {} + elif GlobalState.get_level_completed(level_name): for frog_number in freed_frogs: - if progress_dict[levelName]["froggies"].has(frog_number): - progress_dict[levelName]["froggies"][frog_number] = true + if progress_dict[level_name]["froggies"].has(frog_number): + progress_dict[level_name]["froggies"][frog_number] = true # TODO Wallet is independant from progress_dict because??? GlobalState.set_wallet(GlobalState.gsr.wallet + currency) GlobalState.set_progress(progress_dict) - func player_dying(animation_number: int = 0) -> void: currency = 0 is_dead = true diff --git a/src/Utilities/LevelState.tscn b/src/Utilities/LevelState.tscn index 0837704..a2ab10a 100644 --- a/src/Utilities/LevelState.tscn +++ b/src/Utilities/LevelState.tscn @@ -3,4 +3,5 @@ [ext_resource path="res://src/Utilities/LevelState.gd" type="Script" id=1] [node name="LevelState" type="Node"] +process_priority = -2 script = ExtResource( 1 )