saves and score are added

This commit is contained in:
2025-09-25 22:47:59 +04:00
parent ea8c85d7ad
commit f858f9b633
13 changed files with 1190 additions and 118 deletions

View File

@@ -12,8 +12,16 @@ extends Control
@export var target_script_path: String = "res://scenes/game/gameplays/match3_gameplay.gd"
@export var log_category: String = "DebugMenu"
# Safety constants matching match3_gameplay.gd
const MAX_GRID_SIZE := 15
const MAX_TILE_TYPES := 10
const MIN_GRID_SIZE := 3
const MIN_TILE_TYPES := 3
var match3_scene: Node2D
var search_timer: Timer
var last_scene_search_time: float = 0.0
const SCENE_SEARCH_COOLDOWN := 0.5 # Prevent excessive scene searching
func _exit_tree():
if search_timer:
@@ -43,27 +51,27 @@ func _ready():
_find_target_scene()
func _initialize_spinboxes():
# Initialize gem types spinbox
gem_types_spinbox.min_value = 3
gem_types_spinbox.max_value = 8
# Initialize gem types spinbox with safety limits
gem_types_spinbox.min_value = MIN_TILE_TYPES
gem_types_spinbox.max_value = MAX_TILE_TYPES
gem_types_spinbox.step = 1
gem_types_spinbox.value = 5 # Default value
# Initialize grid size spinboxes
grid_width_spinbox.min_value = 4
grid_width_spinbox.max_value = 12
# Initialize grid size spinboxes with safety limits
grid_width_spinbox.min_value = MIN_GRID_SIZE
grid_width_spinbox.max_value = MAX_GRID_SIZE
grid_width_spinbox.step = 1
grid_width_spinbox.value = 8 # Default value
grid_height_spinbox.min_value = 4
grid_height_spinbox.max_value = 12
grid_height_spinbox.min_value = MIN_GRID_SIZE
grid_height_spinbox.max_value = MAX_GRID_SIZE
grid_height_spinbox.step = 1
grid_height_spinbox.value = 8 # Default value
func _setup_scene_finding():
# Create timer for periodic scene search
# Create timer for periodic scene search with longer intervals to reduce CPU usage
search_timer = Timer.new()
search_timer.wait_time = 0.1
search_timer.wait_time = 0.5 # Reduced frequency from 0.1 to 0.5 seconds
search_timer.timeout.connect(_find_target_scene)
add_child(search_timer)
@@ -92,6 +100,11 @@ func _update_ui_from_scene():
if not match3_scene:
return
# Connect to grid state loaded signal if not already connected
if match3_scene.has_signal("grid_state_loaded") and not match3_scene.grid_state_loaded.is_connected(_on_grid_state_loaded):
match3_scene.grid_state_loaded.connect(_on_grid_state_loaded)
DebugManager.log_debug("Connected to grid_state_loaded signal", log_category)
# Update gem types display
if match3_scene.has_method("get") and "TILE_TYPES" in match3_scene:
gem_types_spinbox.value = match3_scene.TILE_TYPES
@@ -105,6 +118,18 @@ func _update_ui_from_scene():
grid_width_label.text = "Width: " + str(grid_size.x)
grid_height_label.text = "Height: " + str(grid_size.y)
func _on_grid_state_loaded(grid_size: Vector2i, tile_types: int):
DebugManager.log_debug("Grid state loaded signal received: size=%s, types=%d" % [grid_size, tile_types], log_category)
# Update the UI with the actual loaded values
gem_types_spinbox.value = tile_types
gem_types_label.text = "Gem Types: " + str(tile_types)
grid_width_spinbox.value = grid_size.x
grid_height_spinbox.value = grid_size.y
grid_width_label.text = "Width: " + str(grid_size.x)
grid_height_label.text = "Height: " + str(grid_size.y)
func _stop_search_timer():
if search_timer and search_timer.timeout.is_connected(_find_target_scene):
search_timer.stop()
@@ -122,6 +147,14 @@ func _on_debug_toggled(enabled: bool):
if not match3_scene:
_find_target_scene()
_update_ui_from_scene()
# Force refresh the values in case they changed while debug was hidden
_refresh_current_values()
func _refresh_current_values():
# Refresh UI with current values from the scene
if match3_scene:
DebugManager.log_debug("Refreshing debug menu values from current scene state", log_category)
_update_ui_from_scene()
func _on_regenerate_pressed():
if not match3_scene:
@@ -138,17 +171,25 @@ func _on_regenerate_pressed():
DebugManager.log_error("Target scene does not have regenerate_grid method", log_category)
func _on_gem_types_changed(value: float):
# Rate limiting for scene searches
var current_time = Time.get_ticks_msec() / 1000.0
if current_time - last_scene_search_time < SCENE_SEARCH_COOLDOWN:
return
if not match3_scene:
_find_target_scene()
last_scene_search_time = current_time
if not match3_scene:
DebugManager.log_error("Could not find target scene for gem types change", log_category)
return
var new_value = int(value)
# Input validation
if new_value < gem_types_spinbox.min_value or new_value > gem_types_spinbox.max_value:
DebugManager.log_error("Invalid gem types value: %d (range: %d-%d)" % [new_value, gem_types_spinbox.min_value, gem_types_spinbox.max_value], log_category)
# Enhanced input validation with safety constants
if new_value < MIN_TILE_TYPES or new_value > MAX_TILE_TYPES:
DebugManager.log_error("Invalid gem types value: %d (range: %d-%d)" % [new_value, MIN_TILE_TYPES, MAX_TILE_TYPES], log_category)
# Reset to valid value
gem_types_spinbox.value = clamp(new_value, MIN_TILE_TYPES, MAX_TILE_TYPES)
return
if match3_scene.has_method("set_tile_types"):
@@ -163,17 +204,25 @@ func _on_gem_types_changed(value: float):
gem_types_label.text = "Gem Types: " + str(new_value)
func _on_grid_width_changed(value: float):
# Rate limiting for scene searches
var current_time = Time.get_ticks_msec() / 1000.0
if current_time - last_scene_search_time < SCENE_SEARCH_COOLDOWN:
return
if not match3_scene:
_find_target_scene()
last_scene_search_time = current_time
if not match3_scene:
DebugManager.log_error("Could not find target scene for grid width change", log_category)
return
var new_width = int(value)
# Input validation
if new_width < grid_width_spinbox.min_value or new_width > grid_width_spinbox.max_value:
DebugManager.log_error("Invalid grid width value: %d (range: %d-%d)" % [new_width, grid_width_spinbox.min_value, grid_width_spinbox.max_value], log_category)
# Enhanced input validation with safety constants
if new_width < MIN_GRID_SIZE or new_width > MAX_GRID_SIZE:
DebugManager.log_error("Invalid grid width value: %d (range: %d-%d)" % [new_width, MIN_GRID_SIZE, MAX_GRID_SIZE], log_category)
# Reset to valid value
grid_width_spinbox.value = clamp(new_width, MIN_GRID_SIZE, MAX_GRID_SIZE)
return
grid_width_label.text = "Width: " + str(new_width)
@@ -188,17 +237,25 @@ func _on_grid_width_changed(value: float):
DebugManager.log_error("Target scene does not have set_grid_size method", log_category)
func _on_grid_height_changed(value: float):
# Rate limiting for scene searches
var current_time = Time.get_ticks_msec() / 1000.0
if current_time - last_scene_search_time < SCENE_SEARCH_COOLDOWN:
return
if not match3_scene:
_find_target_scene()
last_scene_search_time = current_time
if not match3_scene:
DebugManager.log_error("Could not find target scene for grid height change", log_category)
return
var new_height = int(value)
# Input validation
if new_height < grid_height_spinbox.min_value or new_height > grid_height_spinbox.max_value:
DebugManager.log_error("Invalid grid height value: %d (range: %d-%d)" % [new_height, grid_height_spinbox.min_value, grid_height_spinbox.max_value], log_category)
# Enhanced input validation with safety constants
if new_height < MIN_GRID_SIZE or new_height > MAX_GRID_SIZE:
DebugManager.log_error("Invalid grid height value: %d (range: %d-%d)" % [new_height, MIN_GRID_SIZE, MAX_GRID_SIZE], log_category)
# Reset to valid value
grid_height_spinbox.value = clamp(new_height, MIN_GRID_SIZE, MAX_GRID_SIZE)
return
grid_height_label.text = "Height: " + str(new_height)

View File

@@ -9,11 +9,17 @@ var original_button_scales: Array[Vector2] = []
func _ready():
DebugManager.log_info("MainMenu ready", "MainMenu")
_setup_menu_navigation()
_update_new_game_button()
func _on_new_game_button_pressed():
AudioManager.play_ui_click()
DebugManager.log_info("New Game pressed", "MainMenu")
GameManager.start_new_game()
var button_text = $MenuContainer/NewGameButton.text
if button_text == "Continue":
DebugManager.log_info("Continue pressed", "MainMenu")
GameManager.continue_game()
else:
DebugManager.log_info("New Game pressed", "MainMenu")
GameManager.start_new_game()
func _on_settings_button_pressed():
AudioManager.play_ui_click()
@@ -77,3 +83,17 @@ func _update_visual_selection():
else:
button.scale = original_button_scales[i]
button.modulate = Color.WHITE
func _update_new_game_button():
# Check if there's an existing save with progress
var current_score = SaveManager.get_current_score()
var games_played = SaveManager.get_games_played()
var has_saved_grid = SaveManager.has_saved_grid()
var new_game_button = $MenuContainer/NewGameButton
if current_score > 0 or games_played > 0 or has_saved_grid:
new_game_button.text = "Continue"
DebugManager.log_info("Updated button to Continue (score: %d, games: %d, grid: %s)" % [current_score, games_played, has_saved_grid], "MainMenu")
else:
new_game_button.text = "New Game"
DebugManager.log_info("Updated button to New Game", "MainMenu")

View File

@@ -6,10 +6,14 @@ signal back_to_main_menu
@onready var music_slider = $SettingsContainer/MusicVolumeContainer/MusicVolumeSlider
@onready var sfx_slider = $SettingsContainer/SFXVolumeContainer/SFXVolumeSlider
@onready var language_stepper = $SettingsContainer/LanguageContainer/LanguageStepper
@onready var reset_progress_button = $ResetSettingsContainer/ResetProgressButton
@export var settings_manager: Node = SettingsManager
@export var localization_manager: Node = LocalizationManager
# Progress reset confirmation dialog
var confirmation_dialog: AcceptDialog
# Navigation system variables
var navigable_controls: Array[Control] = []
@@ -39,6 +43,7 @@ func _ready():
_update_controls_from_settings()
update_text()
_setup_navigation_system()
_setup_confirmation_dialog()
func _update_controls_from_settings():
master_slider.value = settings_manager.get_setting("master_volume")
@@ -112,6 +117,7 @@ func update_text():
$SettingsContainer/SFXVolumeContainer/SFXVolume.text = tr("sfx_volume")
$SettingsContainer/LanguageContainer/LanguageLabel.text = tr("language")
$BackButtonContainer/BackButton.text = tr("back")
reset_progress_button.text = tr("reset_progress")
func _on_reset_setting_button_pressed() -> void:
@@ -133,6 +139,7 @@ func _setup_navigation_system():
navigable_controls.append(language_stepper) # Use the ValueStepper component
navigable_controls.append($BackButtonContainer/BackButton)
navigable_controls.append($ResetSettingsContainer/ResetSettingButton)
navigable_controls.append(reset_progress_button)
# Store original visual properties
for control in navigable_controls:
@@ -218,3 +225,73 @@ func _get_control_name(control: Control) -> String:
func _on_language_stepper_value_changed(new_value: String, new_index: int):
DebugManager.log_info("Language changed via ValueStepper: " + new_value + " (index: " + str(new_index) + ")", "Settings")
func _setup_confirmation_dialog():
"""Create confirmation dialog for progress reset"""
confirmation_dialog = AcceptDialog.new()
confirmation_dialog.title = tr("confirm_reset_title")
confirmation_dialog.dialog_text = tr("confirm_reset_message")
confirmation_dialog.ok_button_text = tr("reset_confirm")
confirmation_dialog.add_cancel_button(tr("cancel"))
# Make dialog modal and centered
confirmation_dialog.set_flag(Window.FLAG_POPUP, true)
confirmation_dialog.popup_window = true
# Connect signals
confirmation_dialog.confirmed.connect(_on_reset_progress_confirmed)
confirmation_dialog.canceled.connect(_on_reset_progress_canceled)
add_child(confirmation_dialog)
func _on_reset_progress_button_pressed():
"""Handle reset progress button press with confirmation"""
AudioManager.play_ui_click()
DebugManager.log_info("Reset progress button pressed", "Settings")
# Update dialog text with current translations
confirmation_dialog.title = tr("confirm_reset_title")
confirmation_dialog.dialog_text = tr("confirm_reset_message")
confirmation_dialog.ok_button_text = tr("reset_confirm")
# Show confirmation dialog
confirmation_dialog.popup_centered()
func _on_reset_progress_confirmed():
"""Actually reset the progress after confirmation"""
AudioManager.play_ui_click()
DebugManager.log_info("Progress reset confirmed by user", "Settings")
# Call SaveManager to reset all progress
if SaveManager.reset_all_progress():
DebugManager.log_info("All progress successfully reset", "Settings")
# Show success message
var success_dialog = AcceptDialog.new()
success_dialog.title = tr("reset_success_title")
success_dialog.dialog_text = tr("reset_success_message")
success_dialog.ok_button_text = tr("ok")
add_child(success_dialog)
success_dialog.popup_centered()
# Auto-close success dialog and remove it after 3 seconds
success_dialog.confirmed.connect(func(): success_dialog.queue_free())
await get_tree().create_timer(3.0).timeout
if is_instance_valid(success_dialog):
success_dialog.queue_free()
else:
DebugManager.log_error("Failed to reset progress", "Settings")
# Show error message
var error_dialog = AcceptDialog.new()
error_dialog.title = tr("reset_error_title")
error_dialog.dialog_text = tr("reset_error_message")
error_dialog.ok_button_text = tr("ok")
add_child(error_dialog)
error_dialog.popup_centered()
error_dialog.confirmed.connect(func(): error_dialog.queue_free())
func _on_reset_progress_canceled():
"""Handle reset progress cancellation"""
AudioManager.play_ui_click()
DebugManager.log_info("Progress reset canceled by user", "Settings")

View File

@@ -2,7 +2,7 @@
[ext_resource type="Script" uid="uid://dftenhuhwskqa" path="res://scenes/ui/SettingsMenu.gd" id="1_oqkcn"]
[ext_resource type="PackedScene" uid="uid://df2b4wn8j6cxl" path="res://scenes/ui/DebugToggle.tscn" id="2_debug"]
[ext_resource type="PackedScene" path="res://scenes/ui/components/ValueStepper.tscn" id="3_value_stepper"]
[ext_resource type="PackedScene" uid="uid://cb6k05r8t7l4l" path="res://scenes/ui/components/ValueStepper.tscn" id="3_value_stepper"]
[node name="SettingsMenu" type="Control" groups=["localizable"]]
layout_mode = 3
@@ -26,6 +26,7 @@ offset_right = 34.5
offset_bottom = 20.0
grow_horizontal = 2
grow_vertical = 2
alignment = 1
[node name="SettingsTitle" type="Label" parent="SettingsContainer"]
custom_minimum_size = Vector2(300, 0)
@@ -105,30 +106,28 @@ grow_horizontal = 2
grow_vertical = 2
text = "back"
[node name="ResetSettingsContainer" type="Control" parent="."]
[node name="ResetSettingsContainer" type="VBoxContainer" parent="."]
layout_mode = 1
anchors_preset = 7
anchor_left = 0.5
anchor_top = 1.0
anchor_right = 0.5
anchor_bottom = 1.0
offset_left = -20.0
offset_top = -40.0
offset_right = 20.0
offset_left = -98.0
offset_top = -80.0
offset_right = 98.0
grow_horizontal = 2
grow_vertical = 0
[node name="ResetSettingButton" type="Button" parent="ResetSettingsContainer"]
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -98.0
offset_right = 98.0
offset_bottom = 31.0
grow_horizontal = 2
layout_mode = 2
text = "Reset settings to default"
[node name="ResetProgressButton" type="Button" parent="ResetSettingsContainer"]
modulate = Color(1, 0.7, 0.7, 1)
layout_mode = 2
text = "Reset All Progress"
[node name="DebugToggle" parent="." instance=ExtResource("2_debug")]
layout_mode = 1
@@ -138,3 +137,4 @@ layout_mode = 1
[connection signal="value_changed" from="SettingsContainer/LanguageContainer/LanguageStepper" to="." method="_on_language_stepper_value_changed"]
[connection signal="pressed" from="BackButtonContainer/BackButton" to="." method="_on_back_button_pressed"]
[connection signal="pressed" from="ResetSettingsContainer/ResetSettingButton" to="." method="_on_reset_setting_button_pressed"]
[connection signal="pressed" from="ResetSettingsContainer/ResetProgressButton" to="." method="_on_reset_progress_button_pressed"]