diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 15cf091..bc9ee05 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -2,7 +2,8 @@ "permissions": { "allow": [ "WebSearch", - "Bash(find:*)" + "Bash(find:*)", + "Bash(godot:*)" ], "deny": [], "ask": [] diff --git a/docs/CLAUDE.md b/docs/CLAUDE.md index 05a015c..d45d273 100644 --- a/docs/CLAUDE.md +++ b/docs/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -"Skelly" is a Godot 4.4 mobile game project featuring a match-3 puzzle game within a broader game framework. The project includes a menu system, settings management, audio handling, localization support, and a comprehensive debug system. +"Skelly" is a Godot 4.4 mobile game project featuring multiple gameplay modes within a unified game framework. The project currently supports match-3 puzzle gameplay with planned support for clickomania gameplay. It includes a modular gameplay system, menu system, settings management, audio handling, localization support, and a comprehensive debug system. **For detailed project architecture, see `docs/MAP.md`** @@ -17,9 +17,10 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co - Debug UI: Press F12 in-game or use debug button to toggle debug panels ### Testing & Development -- Debug mode can be toggled with F12 key or debug button UI +- Debug mode can be toggled with F12 key or debug button UI (available on all major scenes) - Match-3 debug controls include gem count adjustment and board reroll - Difficulty presets: Easy (3 gems), Normal (5 gems), Hard (8 gems) +- Gameplay mode switching: Space+Enter in game scene switches between match-3 and clickomania modes ### Audio Configuration - Music: Located in `assets/audio/music/` directory with loop configuration in AudioManager @@ -37,6 +38,8 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co - **ALWAYS** use `GameManager` for scene transitions - never call `get_tree().change_scene_to_file()` directly - Scene paths are defined as constants in GameManager - Error handling is built into GameManager for failed scene loads +- Use `GameManager.start_game_with_mode(mode)` to launch specific gameplay modes +- Supported gameplay modes: "match3", "clickomania" ### Autoload Usage - Use autoloads for global state management only @@ -56,9 +59,11 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co - **This file** - Claude Code specific development guidelines ### Key Scripts to Understand -- `src/autoloads/GameManager.gd` - Scene transition patterns +- `src/autoloads/GameManager.gd` - Scene transition patterns and gameplay mode management - `src/autoloads/DebugManager.gd` - Debug system integration -- `scenes/match3/match3.gd` - Match-3 implementation reference +- `scenes/game/game.gd` - Main game scene with modular gameplay system +- `scenes/game/gameplays/match3_gameplay.gd` - Match-3 implementation reference +- `scenes/game/gameplays/` - Individual gameplay mode implementations - `project.godot` - Input actions and autoload definitions ## Development Workflow @@ -75,8 +80,10 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co - Check mobile compatibility if UI changes made ### Common Implementation Patterns -- Scene transitions: Use `GameManager.change_to_scene()` methods -- Debug integration: Connect to `DebugManager` signals +- Scene transitions: Use `GameManager.start_game_with_mode()` and related methods +- Debug integration: Connect to `DebugManager` signals and initialize debug state +- Gameplay modes: Implement in `scenes/game/gameplays/` directory following modular pattern +- Scoring system: Connect `score_changed` signal from gameplay to main game scene - Settings: Use `SettingsManager` for persistent configuration - Audio: Use `AudioManager` for music and sound effects - Localization: Use `LocalizationManager` for language switching \ No newline at end of file diff --git a/docs/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md index 7682335..275fa74 100644 --- a/docs/CODE_OF_CONDUCT.md +++ b/docs/CODE_OF_CONDUCT.md @@ -100,7 +100,7 @@ func _get_match_line(start: Vector2i, dir: Vector2i) -> Array: GameManager.start_match3_game() # ❌ Wrong -get_tree().change_scene_to_file("res://scenes/match3/match3.tscn") +GameManager.start_match3_game() # Use GameManager instead of direct scene loading ``` ### Autoload Usage @@ -227,7 +227,7 @@ wip get_tree().change_scene_to_file("some_scene.tscn") # ❌ Don't hardcode paths -var tile = load("res://scenes/match3/tile.tscn") +var tile = load("res://scenes/game/gameplays/tile.tscn") # ❌ Don't ignore null checks var node = get_node("SomeNode") diff --git a/docs/MAP.md b/docs/MAP.md index 2c8d764..9131099 100644 --- a/docs/MAP.md +++ b/docs/MAP.md @@ -1,7 +1,7 @@ # Skelly - Project Structure Map ## Overview -Skelly is a Godot 4.4 game project featuring a match-3 puzzle game with skeleton character themes. The project follows a modular architecture with clear separation between scenes, autoloads, assets, and data. +Skelly is a Godot 4.4 game project featuring multiple gameplay modes with skeleton character themes. The project supports match-3 puzzle gameplay with planned clickomania gameplay through a modular gameplay architecture. It follows a modular structure with clear separation between scenes, autoloads, assets, and data. ## Project Root Structure @@ -36,10 +36,11 @@ Located in `src/autoloads/`, these scripts are automatically loaded when the gam - Uses: `data/default_bus_layout.tres` 3. **GameManager** (`src/autoloads/GameManager.gd`) - - Central game state management - - Scene transitions between main/game/match3 + - Central game state management and gameplay mode coordination + - Scene transitions between main/game scenes + - Gameplay mode selection and launching (match3, clickomania) - Navigation flow control - - References: main.tscn, game.tscn, match3.tscn + - References: main.tscn, game.tscn and individual gameplay scenes 4. **LocalizationManager** (`src/autoloads/LocalizationManager.gd`) - Language switching functionality @@ -60,6 +61,12 @@ main.tscn (Entry Point) ├── PressAnyKeyScreen.tscn ├── MainMenu.tscn └── SettingsMenu.tscn + +game.tscn (Gameplay Container) +├── GameplayContainer (Dynamic content) +├── UI/ScoreDisplay +├── BackButton +└── DebugToggle ``` ### Game Flow @@ -86,48 +93,61 @@ main.tscn (Entry Point) - Connected to SettingsManager and AudioManager 5. **Game Scene** (`scenes/game/game.tscn` + `game.gd`) - - Main gameplay container - - Loads match-3 instance - - Back button for navigation - - Bridge between UI and game logic - -6. **Match-3 Game** (`scenes/match3/match3.tscn` + `match3.gd`) - - Core puzzle game implementation - - Grid-based tile system - - Debug UI integration - - Gem management system + - Main gameplay container with modular gameplay system + - Dynamic loading of gameplay modes into GameplayContainer + - Global score management and display + - Back button for navigation to main menu + - Gameplay mode switching support (Space+Enter debug key) + - Bridge between UI and individual gameplay implementations ### UI Components ``` scenes/ui/ -├── DebugButton.tscn + DebugButton.gd +├── DebugToggle.tscn + DebugToggle.gd # Now available on all major scenes +├── DebugMenu.tscn + DebugMenu.gd # Match-3 debug controls ├── MainMenu.tscn + MainMenu.gd └── SettingsMenu.tscn + SettingsMenu.gd ``` -## Match-3 Game System +## Modular Gameplay System -### Core Components -1. **Match3 Controller** (`scenes/match3/match3.gd`) +The game now uses a modular gameplay architecture where different game modes can be dynamically loaded into the main game scene. + +### Gameplay Architecture +- **Main Game Scene** (`scenes/game/game.gd`) - Container and coordinator +- **Gameplay Directory** (`scenes/game/gameplays/`) - Individual gameplay implementations +- **Dynamic Loading** - Gameplay scenes loaded at runtime based on mode selection +- **Signal-based Communication** - Gameplays communicate with main scene via signals + +### Current Gameplay Modes + +#### Match-3 Mode (`scenes/game/gameplays/match3_gameplay.tscn`) +1. **Match3 Controller** (`scenes/game/gameplays/match3_gameplay.gd`) - Grid management (8x8 default) - Match detection algorithms - Tile dropping and refilling - Gem pool management (3-8 gem types) - Debug UI integration + - Score reporting via `score_changed` signal -2. **Tile System** (`scenes/match3/tile.gd` + `Tile.tscn`) +2. **Tile System** (`scenes/game/gameplays/tile.gd` + `Tile.tscn`) - Individual tile behavior - Gem type management - Visual representation - Group membership for coordination +#### Clickomania Mode (`scenes/game/gameplays/clickomania_gameplay.tscn`) +- Planned implementation for clickomania-style gameplay +- Will integrate with same scoring and UI systems as match-3 + ### Debug System -- Global debug state via DebugManager -- Match-3 specific debug UI panel -- Gem count controls (+/- buttons) -- Difficulty presets (Easy: 3 gems, Normal: 5 gems, Hard: 8 gems) -- Reroll functionality -- F12 toggle support +- Global debug state via DebugManager with proper initialization +- Debug toggle available on all major scenes (MainMenu, SettingsMenu, PressAnyKeyScreen, Game) +- Match-3 specific debug UI panel with gem count controls and difficulty presets +- Gem count controls (+/- buttons) with difficulty presets (Easy: 3, Normal: 5, Hard: 8) +- Board reroll functionality for testing +- F12 toggle support across all scenes +- Debug prints reduced in production code ## Asset Organization @@ -174,14 +194,18 @@ assets/ PressAnyKeyScreen --[any_key_pressed]--> Main MainMenu --[open_settings]--> Main SettingsMenu --[back_to_main_menu]--> Main -DebugManager --[debug_toggled, debug_ui_toggled]--> Match3 +DebugManager --[debug_toggled]--> All scenes with DebugToggle +GameplayModes --[score_changed]--> Game Scene +Game Scene --[score updates]--> UI/ScoreDisplay ``` ### Scene References ``` -GameManager --> main.tscn, game.tscn, match3.tscn +GameManager --> main.tscn, game.tscn +GameManager --> scenes/game/gameplays/*.tscn (via GAMEPLAY_SCENES constant) Main --> MainMenu.tscn, SettingsMenu.tscn -Game --> match3.tscn (instantiated) +Game --> GameplayContainer (dynamic loading of gameplay scenes) +Game --> scenes/game/gameplays/match3_gameplay.tscn, clickomania_gameplay.tscn ``` ### Asset Dependencies @@ -194,16 +218,20 @@ AudioManager --> data/default_bus_layout.tres ## Development Notes ### Current Implementation Status +- Modular gameplay system with dynamic loading architecture - Match-3 system with 8x8 grid and configurable gem pools -- Debug UI system with F12 toggle functionality -- Scene transition system via GameManager +- Global scoring system integrated across gameplay modes +- Debug UI system with F12 toggle functionality across all major scenes +- Scene transition system via GameManager with gameplay mode support - Internationalization support for English/Russian ### Architecture Patterns 1. **Autoload Pattern** - Global managers as singletons 2. **Signal-Based Communication** - Loose coupling between components -3. **Scene Composition** - Modular scene loading and management -4. **Data-Driven Configuration** - JSON for settings and translations -5. **Component Architecture** - Reusable UI and game components +3. **Modular Gameplay Architecture** - Dynamic loading of gameplay modes +4. **Scene Composition** - Modular scene loading and management +5. **Data-Driven Configuration** - JSON for settings and translations +6. **Component Architecture** - Reusable UI and game components +7. **Centralized Scoring System** - Global score management across gameplay modes This structure provides a clean separation of concerns, making the codebase maintainable and extensible for future features. \ No newline at end of file diff --git a/scenes/game/game.gd b/scenes/game/game.gd index 65a91d6..d39ec2a 100644 --- a/scenes/game/game.gd +++ b/scenes/game/game.gd @@ -1,17 +1,63 @@ -extends Node +extends Control + +const GAMEPLAY_SCENES = { + "match3": "res://scenes/game/gameplays/match3_gameplay.tscn", + "clickomania": "res://scenes/game/gameplays/clickomania_gameplay.tscn" +} @onready var back_button: Button = $BackButtonContainer/BackButton -# Fixed: Use static Match3 instance from scene instead of dynamic loading -@onready var match3_instance: Node = $Match3 +@onready var gameplay_container: Control = $GameplayContainer +@onready var score_display: Label = $UI/ScoreDisplay + +var current_gameplay_mode: String +var global_score: int = 0 : set = set_global_score func _ready() -> void: if not back_button.pressed.is_connected(_on_back_button_pressed): back_button.pressed.connect(_on_back_button_pressed) - # Fixed: No need to load match3 scene - it's already in the scene tree + # Default to match3 for now + set_gameplay_mode("match3") + +func set_gameplay_mode(mode: String) -> void: + current_gameplay_mode = mode + load_gameplay(mode) + +func load_gameplay(mode: String) -> void: + # Clear existing gameplay + for child in gameplay_container.get_children(): + child.queue_free() + + # Load new gameplay + if GAMEPLAY_SCENES.has(mode): + var gameplay_scene = load(GAMEPLAY_SCENES[mode]) + var gameplay_instance = gameplay_scene.instantiate() + gameplay_container.add_child(gameplay_instance) + + # Connect gameplay signals to shared systems + if gameplay_instance.has_signal("score_changed"): + gameplay_instance.score_changed.connect(_on_score_changed) + +func set_global_score(value: int) -> void: + global_score = value + if score_display: + score_display.text = "Score: " + str(global_score) + +func _on_score_changed(points: int) -> void: + self.global_score += points func _on_back_button_pressed() -> void: + print("Back button pressed in game scene") AudioManager.play_ui_click() GameManager.save_game() - # Fixed: Don't free the static instance, just exit GameManager.exit_to_main_menu() + +func _input(event: InputEvent) -> void: + if event.is_action_pressed("ui_accept") and Input.is_action_pressed("ui_select"): + # Debug: Switch to clickomania when Space+Enter pressed together + if current_gameplay_mode == "match3": + set_gameplay_mode("clickomania") + print("Switched to clickomania gameplay") + else: + set_gameplay_mode("match3") + print("Switched to match3 gameplay") diff --git a/scenes/game/game.tscn b/scenes/game/game.tscn index 8640338..8b44908 100644 --- a/scenes/game/game.tscn +++ b/scenes/game/game.tscn @@ -1,27 +1,74 @@ -[gd_scene load_steps=5 format=3 uid="uid://dmwkyeq2l7u04"] +[gd_scene load_steps=4 format=3 uid="uid://dmwkyeq2l7u04"] [ext_resource type="Script" uid="uid://b16jnk7w22mb" path="res://scenes/game/game.gd" id="1_uwrxv"] -[ext_resource type="PackedScene" uid="uid://b4kv7g7kllwgb" path="res://scenes/match3/match3.tscn" id="2_yqjtg"] [ext_resource type="PackedScene" path="res://scenes/ui/DebugToggle.tscn" id="3_debug"] -[ext_resource type="PackedScene" uid="uid://fax5m4ywp1r3" path="res://scenes/ui/DebugMenu.tscn" id="4_debug_menu"] +[ext_resource type="Texture2D" uid="uid://c8y6tlvcgh2gn" path="res://assets/textures/backgrounds/beanstalk-dark.webp" id="5_background"] -[node name="Game" type="Node"] +[node name="Game" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 script = ExtResource("1_uwrxv") +[node name="Background" type="TextureRect" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +texture = ExtResource("5_background") +stretch_mode = 1 + +[node name="UI" type="Control" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="ScoreDisplay" type="Label" parent="UI"] +layout_mode = 1 +anchors_preset = 2 +anchor_top = 1.0 +anchor_bottom = 1.0 +offset_left = 10.0 +offset_top = -31.0 +offset_right = 110.0 +offset_bottom = -10.0 +grow_vertical = 0 +text = "Score: 0" + +[node name="GameplayContainer" type="Control" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + [node name="BackButtonContainer" type="Control" parent="."] -layout_mode = 3 +layout_mode = 1 anchors_preset = 0 -offset_right = 40.0 -offset_bottom = 40.0 +offset_left = 10.0 +offset_top = 10.0 +offset_right = 55.0 +offset_bottom = 41.0 [node name="BackButton" type="Button" parent="BackButtonContainer"] -layout_mode = 0 -offset_right = 45.0 -offset_bottom = 31.0 +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 text = "back" -[node name="Match3" parent="." instance=ExtResource("2_yqjtg")] - [node name="DebugToggle" parent="." instance=ExtResource("3_debug")] +layout_mode = 1 -[node name="DebugMenu" parent="." instance=ExtResource("4_debug_menu")] +[connection signal="pressed" from="BackButtonContainer/BackButton" to="." method="_on_back_button_pressed"] diff --git a/scenes/game/gameplays/Match3DebugMenu.gd b/scenes/game/gameplays/Match3DebugMenu.gd new file mode 100644 index 0000000..f137bd2 --- /dev/null +++ b/scenes/game/gameplays/Match3DebugMenu.gd @@ -0,0 +1,129 @@ +extends Control + +@onready var regenerate_button: Button = $VBoxContainer/RegenerateButton +@onready var gem_types_spinbox: SpinBox = $VBoxContainer/GemTypesContainer/GemTypesSpinBox +@onready var gem_types_label: Label = $VBoxContainer/GemTypesContainer/GemTypesLabel +@onready var grid_width_spinbox: SpinBox = $VBoxContainer/GridSizeContainer/GridWidthContainer/GridWidthSpinBox +@onready var grid_height_spinbox: SpinBox = $VBoxContainer/GridSizeContainer/GridHeightContainer/GridHeightSpinBox +@onready var grid_width_label: Label = $VBoxContainer/GridSizeContainer/GridWidthContainer/GridWidthLabel +@onready var grid_height_label: Label = $VBoxContainer/GridSizeContainer/GridHeightContainer/GridHeightLabel + +var match3_scene: Node2D + +func _ready(): + print("Match3DebugMenu: _ready() called") + DebugManager.debug_toggled.connect(_on_debug_toggled) + # Initialize with current debug state + var current_debug_state = DebugManager.is_debug_enabled() + print("Match3DebugMenu: Current debug state is ", current_debug_state) + visible = current_debug_state + print("Match3DebugMenu: Initial visibility set to ", visible) + if current_debug_state: + _on_debug_toggled(true) + regenerate_button.pressed.connect(_on_regenerate_pressed) + gem_types_spinbox.value_changed.connect(_on_gem_types_changed) + grid_width_spinbox.value_changed.connect(_on_grid_width_changed) + grid_height_spinbox.value_changed.connect(_on_grid_height_changed) + +func _find_match3_scene(): + # Debug menu is now: Match3 -> UILayer -> Match3DebugMenu + # So we need to go up two levels: get_parent() = UILayer, get_parent().get_parent() = Match3 + var ui_layer = get_parent() + if ui_layer and ui_layer is CanvasLayer: + match3_scene = ui_layer.get_parent() + if match3_scene and match3_scene.get_script(): + var script_path = match3_scene.get_script().resource_path + if script_path == "res://scenes/game/gameplays/match3_gameplay.gd": + print("Debug: Found match3 scene: ", match3_scene.name, " at path: ", match3_scene.get_path()) + return + + # If we couldn't find it, clear the reference + match3_scene = null + print("Debug: Could not find match3_gameplay scene") + +func _on_debug_toggled(enabled: bool): + print("Match3DebugMenu: Debug toggled to ", enabled) + visible = enabled + print("Match3DebugMenu: Visibility set to ", visible) + if enabled: + # Always refresh match3 scene reference when debug menu opens + if not match3_scene: + _find_match3_scene() + # Update display values + if match3_scene and match3_scene.has_method("get") and "TILE_TYPES" in match3_scene: + gem_types_spinbox.value = match3_scene.TILE_TYPES + gem_types_label.text = "Gem Types: " + str(match3_scene.TILE_TYPES) + + # Update grid size display values + if match3_scene and "GRID_SIZE" in match3_scene: + var grid_size = match3_scene.GRID_SIZE + 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 _on_regenerate_pressed(): + if not match3_scene: + _find_match3_scene() + + if not match3_scene: + print("Error: Could not find match3 scene for regeneration") + return + + if match3_scene.has_method("regenerate_grid"): + print("Debug: Calling regenerate_grid()") + await match3_scene.regenerate_grid() + else: + print("Error: match3_scene does not have regenerate_grid method") + +func _on_gem_types_changed(value: float): + if not match3_scene: + _find_match3_scene() + + if not match3_scene: + print("Error: Could not find match3 scene for gem types change") + return + + var new_value = int(value) + if match3_scene.has_method("set_tile_types"): + print("Debug: Setting tile types to ", new_value) + await match3_scene.set_tile_types(new_value) + gem_types_label.text = "Gem Types: " + str(new_value) + else: + print("Error: match3_scene does not have set_tile_types method") + +func _on_grid_width_changed(value: float): + if not match3_scene: + _find_match3_scene() + + if not match3_scene: + print("Error: Could not find match3 scene for grid width change") + return + + var new_width = int(value) + grid_width_label.text = "Width: " + str(new_width) + + # Get current height + var current_height = int(grid_height_spinbox.value) + + if match3_scene.has_method("set_grid_size"): + print("Debug: Setting grid size to ", new_width, "x", current_height) + await match3_scene.set_grid_size(Vector2i(new_width, current_height)) + +func _on_grid_height_changed(value: float): + if not match3_scene: + _find_match3_scene() + + if not match3_scene: + print("Error: Could not find match3 scene for grid height change") + return + + var new_height = int(value) + grid_height_label.text = "Height: " + str(new_height) + + # Get current width + var current_width = int(grid_width_spinbox.value) + + if match3_scene.has_method("set_grid_size"): + print("Debug: Setting grid size to ", current_width, "x", new_height) + await match3_scene.set_grid_size(Vector2i(current_width, new_height)) diff --git a/scenes/game/gameplays/Match3DebugMenu.tscn b/scenes/game/gameplays/Match3DebugMenu.tscn new file mode 100644 index 0000000..332f6bc --- /dev/null +++ b/scenes/game/gameplays/Match3DebugMenu.tscn @@ -0,0 +1,83 @@ +[gd_scene load_steps=2 format=3 uid="uid://b76oiwlifikl3"] + +[ext_resource type="Script" path="res://scenes/game/gameplays/Match3DebugMenu.gd" id="1_debug_menu"] + +[node name="DebugMenu" type="Control"] +layout_mode = 3 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -201.0 +offset_top = 59.0 +offset_right = -11.0 +offset_bottom = 169.0 +grow_horizontal = 0 +script = ExtResource("1_debug_menu") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 10.0 +offset_top = 10.0 +offset_right = -10.0 +offset_bottom = -10.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="TitleLabel" type="Label" parent="VBoxContainer"] +layout_mode = 2 +text = "Match-3 Debug Menu" +horizontal_alignment = 1 + +[node name="RegenerateButton" type="Button" parent="VBoxContainer"] +layout_mode = 2 +text = "Generate New Grid" + +[node name="GemTypesContainer" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="GemTypesLabel" type="Label" parent="VBoxContainer/GemTypesContainer"] +layout_mode = 2 +text = "Gem Types: 5" + +[node name="GemTypesSpinBox" type="SpinBox" parent="VBoxContainer/GemTypesContainer"] +layout_mode = 2 +min_value = 3.0 +max_value = 8.0 +value = 5.0 + +[node name="GridSizeContainer" type="VBoxContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="GridSizeLabel" type="Label" parent="VBoxContainer/GridSizeContainer"] +layout_mode = 2 +text = "Grid Size" +horizontal_alignment = 1 + +[node name="GridWidthContainer" type="HBoxContainer" parent="VBoxContainer/GridSizeContainer"] +layout_mode = 2 + +[node name="GridWidthLabel" type="Label" parent="VBoxContainer/GridSizeContainer/GridWidthContainer"] +layout_mode = 2 +text = "Width: 8" + +[node name="GridWidthSpinBox" type="SpinBox" parent="VBoxContainer/GridSizeContainer/GridWidthContainer"] +layout_mode = 2 +min_value = 4.0 +max_value = 12.0 +value = 8.0 + +[node name="GridHeightContainer" type="HBoxContainer" parent="VBoxContainer/GridSizeContainer"] +layout_mode = 2 + +[node name="GridHeightLabel" type="Label" parent="VBoxContainer/GridSizeContainer/GridHeightContainer"] +layout_mode = 2 +text = "Height: 8" + +[node name="GridHeightSpinBox" type="SpinBox" parent="VBoxContainer/GridSizeContainer/GridHeightContainer"] +layout_mode = 2 +min_value = 4.0 +max_value = 12.0 +value = 8.0 diff --git a/scenes/match3/Tile.tscn b/scenes/game/gameplays/Tile.tscn similarity index 62% rename from scenes/match3/Tile.tscn rename to scenes/game/gameplays/Tile.tscn index aad47c4..6747650 100644 --- a/scenes/match3/Tile.tscn +++ b/scenes/game/gameplays/Tile.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=2 format=3 uid="uid://bnk1gqom3oi6q"] -[ext_resource type="Script" uid="uid://3bu5wgfarlgp" path="res://scenes/match3/tile.gd" id="1_tile_script"] +[ext_resource type="Script" path="res://scenes/game/gameplays/tile.gd" id="1_tile_script"] [node name="Tile" type="Node2D"] script = ExtResource("1_tile_script") diff --git a/scenes/game/gameplays/clickomania_gameplay.gd b/scenes/game/gameplays/clickomania_gameplay.gd new file mode 100644 index 0000000..30add9c --- /dev/null +++ b/scenes/game/gameplays/clickomania_gameplay.gd @@ -0,0 +1,10 @@ +extends Node2D + +signal score_changed(points: int) + +func _ready(): + print("Clickomania gameplay loaded") + # Example: Add some score after a few seconds to test the system + await get_tree().create_timer(2.0).timeout + score_changed.emit(100) + print("Clickomania awarded 100 points") \ No newline at end of file diff --git a/scenes/game/gameplays/clickomania_gameplay.tscn b/scenes/game/gameplays/clickomania_gameplay.tscn new file mode 100644 index 0000000..3cc5220 --- /dev/null +++ b/scenes/game/gameplays/clickomania_gameplay.tscn @@ -0,0 +1,21 @@ +[gd_scene load_steps=2 format=3 uid="uid://cl7g8v0eh3mam"] + +[ext_resource type="Script" path="res://scenes/game/gameplays/clickomania_gameplay.gd" id="1_script"] + +[node name="Clickomania" type="Node2D"] +script = ExtResource("1_script") + +[node name="Label" type="Label" parent="."] +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -100.0 +offset_top = -12.0 +offset_right = 100.0 +offset_bottom = 12.0 +grow_horizontal = 2 +grow_vertical = 2 +text = "Clickomania Gameplay (Demo)" +horizontal_alignment = 1 \ No newline at end of file diff --git a/scenes/match3/match3.gd b/scenes/game/gameplays/match3_gameplay.gd similarity index 94% rename from scenes/match3/match3.gd rename to scenes/game/gameplays/match3_gameplay.gd index ef2317e..d82a9e6 100644 --- a/scenes/match3/match3.gd +++ b/scenes/game/gameplays/match3_gameplay.gd @@ -1,8 +1,10 @@ extends Node2D +signal score_changed(points: int) + var GRID_SIZE := Vector2i(8, 8) var TILE_TYPES := 5 -const TILE_SCENE := preload("res://scenes/match3/tile.tscn") +const TILE_SCENE := preload("res://scenes/game/gameplays/tile.tscn") var grid := [] var tile_size: float = 48.0 @@ -14,7 +16,7 @@ func _ready(): for i in range(TILE_TYPES): gem_indices.append(i) - const TileScript = preload("res://scenes/match3/tile.gd") + const TileScript = preload("res://scenes/game/gameplays/tile.gd") TileScript.set_active_gem_pool(gem_indices) _calculate_grid_layout() @@ -43,7 +45,7 @@ func _initialize_grid(): for i in range(TILE_TYPES): gem_indices.append(i) - const TileScript = preload("res://scenes/match3/tile.gd") + const TileScript = preload("res://scenes/game/gameplays/tile.gd") TileScript.set_active_gem_pool(gem_indices) for y in range(GRID_SIZE.y): @@ -56,7 +58,7 @@ func _initialize_grid(): # Set tile type after adding to scene tree so sprite reference is available var new_type = randi() % TILE_TYPES tile.tile_type = new_type - print("Debug: Created tile at (", x, ",", y, ") with type ", new_type) + # print("Debug: Created tile at (", x, ",", y, ") with type ", new_type) grid[y].append(tile) func _has_match_at(pos: Vector2i) -> bool: @@ -182,7 +184,7 @@ func regenerate_grid(): for child in get_children(): if child.has_method("get_script") and child.get_script(): var script_path = child.get_script().resource_path - if script_path == "res://scenes/match3/tile.gd": + if script_path == "res://scenes/game/gameplays/tile.gd": children_to_remove.append(child) # Remove all found tile children diff --git a/scenes/game/gameplays/match3_gameplay.tscn b/scenes/game/gameplays/match3_gameplay.tscn new file mode 100644 index 0000000..b059adc --- /dev/null +++ b/scenes/game/gameplays/match3_gameplay.tscn @@ -0,0 +1,13 @@ +[gd_scene load_steps=3 format=3 uid="uid://b4kv7g7kllwgb"] + +[ext_resource type="Script" path="res://scenes/game/gameplays/match3_gameplay.gd" id="1_mvfdp"] +[ext_resource type="PackedScene" path="res://scenes/game/gameplays/Match3DebugMenu.tscn" id="2_debug_menu"] + +[node name="Match3" type="Node2D"] +script = ExtResource("1_mvfdp") + +[node name="GridContainer" type="Node2D" parent="."] + +[node name="UILayer" type="CanvasLayer" parent="."] + +[node name="Match3DebugMenu" parent="UILayer" instance=ExtResource("2_debug_menu")] diff --git a/scenes/match3/tile.gd b/scenes/game/gameplays/tile.gd similarity index 98% rename from scenes/match3/tile.gd rename to scenes/game/gameplays/tile.gd index 1536971..e1abff4 100644 --- a/scenes/match3/tile.gd +++ b/scenes/game/gameplays/tile.gd @@ -35,7 +35,7 @@ func _set_tile_type(value: int) -> void: if value >= 0 and value < active_gem_types.size(): var texture_index = active_gem_types[value] sprite.texture = all_gem_textures[texture_index] - print("Debug: Set texture_index ", texture_index, " for tile_type ", value) + # print("Debug: Set texture_index ", texture_index, " for tile_type ", value) _scale_sprite_to_fit() else: push_error("Invalid tile type: " + str(value) + ". Available types: 0-" + str(active_gem_types.size() - 1)) diff --git a/scenes/main/PressAnyKeyScreen.tscn b/scenes/main/PressAnyKeyScreen.tscn index d821957..5f57d7c 100644 --- a/scenes/main/PressAnyKeyScreen.tscn +++ b/scenes/main/PressAnyKeyScreen.tscn @@ -1,7 +1,8 @@ -[gd_scene load_steps=15 format=3 uid="uid://gbe1jarrwqsi"] +[gd_scene load_steps=16 format=3 uid="uid://gbe1jarrwqsi"] [ext_resource type="Script" uid="uid://cxw2fjj5onja3" path="res://scenes/main/PressAnyKeyScreen.gd" id="1_0a4p2"] [ext_resource type="Texture2D" uid="uid://bcr4bokw87m5n" path="res://assets/sprites/characters/skeleton/Skeleton Idle.png" id="2_rjjcb"] +[ext_resource type="PackedScene" path="res://scenes/ui/DebugToggle.tscn" id="3_debug"] [sub_resource type="AtlasTexture" id="AtlasTexture_l6pue"] atlas = ExtResource("2_rjjcb") @@ -132,3 +133,6 @@ horizontal_alignment = 1 [node name="PressKeyLabel" type="Label" parent="PressKeyContainer"] layout_mode = 2 text = "`press_ok_continue`" + +[node name="DebugToggle" parent="." instance=ExtResource("3_debug")] +layout_mode = 1 diff --git a/scenes/match3/match3.tscn b/scenes/match3/match3.tscn deleted file mode 100644 index 7524cbe..0000000 --- a/scenes/match3/match3.tscn +++ /dev/null @@ -1,8 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://b4kv7g7kllwgb"] - -[ext_resource type="Script" uid="uid://bvyiwvpepqfdu" path="res://scenes/match3/match3.gd" id="1_mvfdp"] - -[node name="Match3" type="Node2D"] -script = ExtResource("1_mvfdp") - -[node name="GridContainer" type="Node2D" parent="."] diff --git a/scenes/ui/DebugMenu.gd b/scenes/ui/DebugMenu.gd index e1ef3de..ae8e61d 100644 --- a/scenes/ui/DebugMenu.gd +++ b/scenes/ui/DebugMenu.gd @@ -17,8 +17,11 @@ func _exit_tree(): search_timer.queue_free() func _ready(): - visible = false DebugManager.debug_toggled.connect(_on_debug_toggled) + + # Initialize with current debug state + var current_debug_state = DebugManager.is_debug_enabled() + visible = current_debug_state regenerate_button.pressed.connect(_on_regenerate_pressed) gem_types_spinbox.value_changed.connect(_on_gem_types_changed) grid_width_spinbox.value_changed.connect(_on_grid_width_changed) @@ -62,7 +65,7 @@ func _find_match3_scene(): var current_scene = get_tree().current_scene if current_scene: # Try to find match3 by class name first - match3_scene = _find_node_by_script(current_scene, "res://scenes/match3/match3.gd") + match3_scene = _find_node_by_script(current_scene, "res://scenes/game/gameplays/match3_gameplay.gd") # Fallback: search by common node names if not match3_scene: diff --git a/scenes/ui/DebugToggle.gd b/scenes/ui/DebugToggle.gd index 24733f7..8f1f54a 100644 --- a/scenes/ui/DebugToggle.gd +++ b/scenes/ui/DebugToggle.gd @@ -1,10 +1,13 @@ extends Button func _ready(): - text = "Debug: OFF" pressed.connect(_on_pressed) DebugManager.debug_toggled.connect(_on_debug_toggled) + # Initialize with current debug state + var current_state = DebugManager.is_debug_enabled() + text = "Debug: " + ("ON" if current_state else "OFF") + func _on_pressed(): DebugManager.toggle_debug() diff --git a/scenes/ui/MainMenu.tscn b/scenes/ui/MainMenu.tscn index e4aad45..e3f7fd9 100644 --- a/scenes/ui/MainMenu.tscn +++ b/scenes/ui/MainMenu.tscn @@ -1,6 +1,7 @@ -[gd_scene load_steps=2 format=3 uid="uid://m8lf3eh3al5j"] +[gd_scene load_steps=3 format=3 uid="uid://m8lf3eh3al5j"] [ext_resource type="Script" uid="uid://b2c35v0f6rymd" path="res://scenes/ui/MainMenu.gd" id="1_b00nv"] +[ext_resource type="PackedScene" path="res://scenes/ui/DebugToggle.tscn" id="2_debug"] [node name="MainMenu" type="Control"] layout_mode = 3 @@ -37,6 +38,9 @@ text = "Settings" layout_mode = 2 text = "Exit" +[node name="DebugToggle" parent="." instance=ExtResource("2_debug")] +layout_mode = 1 + [connection signal="pressed" from="MenuContainer/NewGameButton" to="." method="_on_new_game_button_pressed"] [connection signal="pressed" from="MenuContainer/SettingsButton" to="." method="_on_settings_button_pressed"] [connection signal="pressed" from="MenuContainer/ExitButton" to="." method="_on_exit_button_pressed"] diff --git a/scenes/ui/SettingsMenu.tscn b/scenes/ui/SettingsMenu.tscn index 80dfed5..7adb80a 100644 --- a/scenes/ui/SettingsMenu.tscn +++ b/scenes/ui/SettingsMenu.tscn @@ -1,6 +1,7 @@ -[gd_scene load_steps=2 format=3 uid="uid://57obmcwyos2g"] +[gd_scene load_steps=3 format=3 uid="uid://57obmcwyos2g"] [ext_resource type="Script" uid="uid://bv56qwni68qo" path="res://scenes/ui/SettingsMenu.gd" id="1_oqkcn"] +[ext_resource type="PackedScene" path="res://scenes/ui/DebugToggle.tscn" id="2_debug"] [node name="SettingsMenu" type="Control" groups=["localizable"]] layout_mode = 3 @@ -103,13 +104,18 @@ popup/item_4/id = 2 [node name="BackButtonContainer" type="Control" parent="."] layout_mode = 1 anchors_preset = 0 -offset_right = 40.0 -offset_bottom = 40.0 +offset_left = 10.0 +offset_top = 10.0 +offset_right = 55.0 +offset_bottom = 41.0 [node name="BackButton" type="Button" parent="BackButtonContainer"] -layout_mode = 0 -offset_right = 8.0 -offset_bottom = 8.0 +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 text = "back" [node name="ResetSettingsContainer" type="Control" parent="."] @@ -136,6 +142,9 @@ offset_bottom = 31.0 grow_horizontal = 2 text = "Reset settings to default" +[node name="DebugToggle" parent="." instance=ExtResource("2_debug")] +layout_mode = 1 + [connection signal="value_changed" from="SettingsContainer/MasterVolumeContainer/MasterVolumeSlider" to="." method="_on_master_volume_changed"] [connection signal="value_changed" from="SettingsContainer/MusicVolumeContainer/MusicVolumeSlider" to="." method="_on_music_volume_changed"] [connection signal="value_changed" from="SettingsContainer/SFXVolumeContainer/SFXVolumeSlider" to="." method="_on_sfx_volume_changed"] diff --git a/src/autoloads/GameManager.gd b/src/autoloads/GameManager.gd index 7318a36..8313b4a 100644 --- a/src/autoloads/GameManager.gd +++ b/src/autoloads/GameManager.gd @@ -2,29 +2,39 @@ extends Node const GAME_SCENE_PATH := "res://scenes/game/game.tscn" const MAIN_SCENE_PATH := "res://scenes/main/main.tscn" -const MATCH3_SCENE_PATH := "res://scenes/match3/match3.tscn" + +var pending_gameplay_mode: String = "match3" func start_new_game() -> void: + start_game_with_mode("match3") + +func start_match3_game() -> void: + start_game_with_mode("match3") + +func start_clickomania_game() -> void: + start_game_with_mode("clickomania") + +func start_game_with_mode(gameplay_mode: String) -> void: + pending_gameplay_mode = gameplay_mode var packed_scene := load(GAME_SCENE_PATH) if not packed_scene or not packed_scene is PackedScene: push_error("Failed to load Game scene at: %s" % GAME_SCENE_PATH) return get_tree().change_scene_to_packed(packed_scene) + # Wait one frame for the scene to be ready, then set gameplay mode + await get_tree().process_frame + if get_tree().current_scene and get_tree().current_scene.has_method("set_gameplay_mode"): + get_tree().current_scene.set_gameplay_mode(pending_gameplay_mode) func save_game() -> void: print("Game saved (mock)") func exit_to_main_menu() -> void: + print("GameManager: Attempting to exit to main menu") var packed_scene := load(MAIN_SCENE_PATH) if not packed_scene or not packed_scene is PackedScene: push_error("Failed to load Main scene at: %s" % MAIN_SCENE_PATH) return - get_tree().change_scene_to_packed(packed_scene) - -func start_match3_game() -> void: - var packed_scene := load(MATCH3_SCENE_PATH) - if not packed_scene or not packed_scene is PackedScene: - push_error("Failed to load Match3 scene at: %s" % MATCH3_SCENE_PATH) - return + print("GameManager: Loading main scene") get_tree().change_scene_to_packed(packed_scene)