Compare commits

...

2 Commits

20 changed files with 1187 additions and 109 deletions

3
.gitignore vendored
View File

@@ -6,3 +6,6 @@
*.uid
*.tmp
*.import~
# hidden files
.*

View File

@@ -4,38 +4,9 @@ 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 that features a match-3 puzzle game within a broader game framework. The project includes a menu system, settings management, audio handling, and localization support.
"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.
## Project Structure
### Core Architecture
- **Autoloaded Singletons**: The project uses four global singletons (autoloads) defined in `project.godot`:
- `SettingsManager` - Handles game settings and configuration
- `AudioManager` - Manages music and sound effects with volume controls
- `GameManager` - Handles scene transitions and game state
- `LocalizationManager` - Manages multi-language support (English/Russian)
### Directory Structure
- `scripts/` - Global singleton scripts and utility classes
- `scenes/` - Game scenes including main game and match-3 components
- `match3/` - Specialized match-3 game implementation
- `ui/` - User interface scenes (MainMenu, SettingsMenu, PressAnyKeyScreen)
- `audio/` - Music and sound effect files
- `resources/` - Game assets (textures, sprites, animations)
- `localization/` - Translation files for internationalization
### Scene Management Flow
1. Main entry point: `scenes/main.tscn` with press-any-key screen
2. Menu navigation: MainMenu → SettingsMenu (with back navigation)
3. Game flow: MainMenu → Game scene → Match3 game integration
4. All scene transitions handled through `GameManager` singleton
### Match-3 Game System
- Located in `scenes/match3/`
- Grid-based (8x8) tile matching system
- Implements match detection, tile dropping, and grid refilling
- Uses tile instances with position-based grid management
- Automatic cascade matching after tile drops
**For detailed project architecture, see `docs/MAP.md`**
## Development Commands
@@ -43,44 +14,69 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
- Open project in Godot Editor: Import `project.godot`
- Run project: Press F5 in Godot Editor or use "Play" button
- Debug: Use Godot's built-in debugger and remote inspector
- 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
- Match-3 debug controls include gem count adjustment and board reroll
- Difficulty presets: Easy (3 gems), Normal (5 gems), Hard (8 gems)
### Audio Configuration
- Music: Located in `audio/` directory with loop configuration in AudioManager
- Music: Located in `assets/audio/music/` directory with loop configuration in AudioManager
- Sound effects: UI clicks and game audio managed through audio bus system
- Audio buses: "Music" and "SFX" buses configured in `default_bus_layout.tres`
- Audio buses: "Music" and "SFX" buses configured in `data/default_bus_layout.tres`
### Localization
- Translations stored in `localization/` as `.translation` files
- Currently supports English and Russian
- New translations: Add to `project.godot` internationalization section
## Key Components
## Key Development Guidelines
### AudioManager (`scripts/AudioManager.gd`)
- Handles background music with looping
- Manages UI sound effects (click sounds)
- Volume control integration with SettingsManager
- Automatic audio bus configuration
### Scene Management
- **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
### Match3 Game (`scenes/match3/match3.gd`)
- 8x8 grid system with 5 tile types
- Match detection in horizontal and vertical directions
- Tile dropping physics with position-based movement
- Recursive match clearing and grid refilling
### Autoload Usage
- Use autoloads for global state management only
- Prefer signals over direct access for loose coupling
- Don't access autoloads from deeply nested components
### Scene Navigation
- All scene changes go through GameManager for consistency
- Scene paths defined as constants in GameManager
- Error handling for failed scene loads
### Debug System Integration
- Connect to `DebugManager.debug_ui_toggled` signal for debug UI visibility
- Use F12 key for global debug toggle
- Remove debug prints before committing unless permanently useful
## Input System
- Custom input actions defined in `project.godot`:
- `ui_pause` - Space key or gamepad button
- `any_key` - Multiple inputs for menu progression
- `ui_menu_toggle` - Escape key or gamepad button
## Important File References
## Asset Attribution
Asset sources documented in `sources.yaml` including:
- Music from Clement Panchout
- Sound effects from Freesound
- Sprites and textures from various itch.io creators
### Documentation Structure
- **`docs/MAP.md`** - Complete project architecture and structure
- **`docs/CODE_OF_CONDUCT.md`** - Coding standards and best practices
- **This file** - Claude Code specific development guidelines
### Key Scripts to Understand
- `src/autoloads/GameManager.gd` - Scene transition patterns
- `src/autoloads/DebugManager.gd` - Debug system integration
- `scenes/match3/match3.gd` - Match-3 implementation reference
- `project.godot` - Input actions and autoload definitions
## Development Workflow
### Before Making Changes
1. Check `docs/MAP.md` for architecture understanding
2. Review `docs/CODE_OF_CONDUCT.md` for coding standards
3. Understand existing patterns before implementing new features
### Testing Changes
- Run project with F5 in Godot Editor
- Test debug UI with F12 toggle
- Verify scene transitions work correctly
- Check mobile compatibility if UI changes made
### Common Implementation Patterns
- Scene transitions: Use `GameManager.change_to_scene()` methods
- Debug integration: Connect to `DebugManager` signals
- Settings: Use `SettingsManager` for persistent configuration
- Audio: Use `AudioManager` for music and sound effects
- Localization: Use `LocalizationManager` for language switching

297
docs/CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,297 @@
# Code of Conduct - Skelly Project
## Overview
This document establishes coding standards and development practices for the Skelly project. These guidelines are designed to help junior developers contribute effectively while maintaining code quality and project consistency.
## Core Principles
### 1. Code Clarity Over Cleverness
- Write code that is easy to read and understand
- Use descriptive variable and function names
- Prefer explicit code over implicit or "clever" solutions
- Comment complex logic and business rules
### 2. Consistency First
- Follow existing code patterns in the project
- Use the same naming conventions throughout
- Maintain consistent indentation and formatting
- Follow Godot's GDScript style guide
### 3. Incremental Development
- Make small, focused commits
- Test changes before committing
- Don't break existing functionality
- Use the debug system to verify your changes
## GDScript Coding Standards
### Naming Conventions
```gdscript
# Variables and functions: snake_case
var player_health: int = 100
func calculate_damage() -> int:
# Constants: SCREAMING_SNAKE_CASE
const MAX_HEALTH := 100
const TILE_SPACING := 54
# Classes: PascalCase
class_name PlayerController
# Signals: past_tense
signal health_changed
signal game_started
# Private functions: prefix with underscore
func _ready():
func _initialize_grid():
```
### File Organization
```gdscript
# 1. extends statement
extends Node2D
# 2. class_name (if applicable)
class_name Match3Controller
# 3. Constants
const GRID_SIZE := Vector2i(8, 8)
# 4. Signals
signal match_found(tiles: Array)
# 5. Variables
var grid := []
@onready var debug_ui = $DebugUI
# 6. Functions (lifecycle first, then custom)
func _ready():
func _process(delta):
func custom_function():
```
### Documentation Requirements
```gdscript
# Required for all public functions
func set_gem_pool(gem_indices: Array) -> void:
"""Set specific gem types as the active pool"""
_update_all_tiles_gem_pool(gem_indices)
print("Set gem pool to: ", gem_indices)
# Document complex algorithms
func _get_match_line(start: Vector2i, dir: Vector2i) -> Array:
"""Find all matching tiles in a line from start position in given direction"""
var line = [grid[start.y][start.x]]
var type = grid[start.y][start.x].tile_type
# Implementation...
```
## Project-Specific Guidelines
### Scene Management
- All scene transitions MUST go through `GameManager`
- Never use `get_tree().change_scene_to_file()` directly
- Define scene paths as constants in GameManager
```gdscript
# ✅ Correct
GameManager.start_match3_game()
# ❌ Wrong
get_tree().change_scene_to_file("res://scenes/match3/match3.tscn")
```
### Autoload Usage
- Use autoloads for global state only
- Don't access autoloads from deeply nested components
- Pass data through signals or direct references when possible
```gdscript
# ✅ Correct - using signals
signal settings_changed
SettingsManager.volume_changed.connect(_on_volume_changed)
# ✅ Also correct - direct access for global state
var current_language = SettingsManager.get_setting("language")
# ❌ Wrong - tight coupling
func some_deep_function():
SettingsManager.set_setting("volume", 0.5) # Too deep in call stack
```
### Debug System Integration
- Always connect to DebugManager signals for debug UI
- Use debug prints with clear prefixes
- Remove debug prints before committing (unless permanently useful)
```gdscript
# ✅ Correct debug integration
func _connect_to_global_debug() -> void:
DebugManager.debug_ui_toggled.connect(_on_debug_ui_toggled)
debug_ui.visible = DebugManager.is_debug_ui_visible()
# ✅ Good debug prints
print("Match3: Debug UI toggled to: ", visible)
print("TileSystem: Initialized ", tiles.size(), " tiles")
# ❌ Bad debug prints
print("test") # Not descriptive
print(some_variable) # No context
```
### Error Handling
- Always check if resources loaded successfully
- Use `push_error()` for critical failures
- Provide fallback behavior when possible
```gdscript
# ✅ Correct error handling
func load_scene(path: String) -> void:
var packed_scene := load(path)
if not packed_scene or not packed_scene is PackedScene:
push_error("Failed to load scene at: %s" % path)
return
get_tree().change_scene_to_packed(packed_scene)
```
## Git Workflow
### Commit Guidelines
- Use clear, descriptive commit messages
- Start with a verb in imperative mood
- Keep first line under 50 characters
- Add body if needed for complex changes
```bash
# ✅ Good commit messages
Add gem pool management to match-3 system
Fix debug UI visibility toggle issue
Update documentation for new debug system
# ❌ Bad commit messages
fix bug
update
wip
```
### Branch Management
- Create feature branches for new functionality
- Use descriptive branch names: `feature/debug-ui`, `fix/tile-positioning`
- Keep branches focused on single features
- Delete branches after merging
### Before Committing
1. Test your changes in the Godot editor
2. Check that existing functionality still works
3. Use the debug system to verify your implementation
4. Run the project and navigate through affected areas
5. Remove temporary debug code
## Code Review Guidelines
### For Reviewers
- Focus on code clarity and maintainability
- Check for adherence to project patterns
- Verify that autoloads are used appropriately
- Ensure debug integration is properly implemented
- Test the changes yourself
### For Contributors
- Explain complex logic in commit messages or comments
- Provide context for why changes were made
- Test edge cases and error conditions
- Ask questions if project patterns are unclear
## Testing Approach
### Manual Testing Requirements
- Test in Godot editor with F5 run
- Verify debug UI works with F12 toggle
- Check scene transitions work correctly
- Test on different screen sizes (mobile target)
- Verify audio and settings integration
### Debug System Testing
- Ensure debug panels appear/disappear correctly
- Test all debug buttons and controls
- Verify debug state persists across scene changes
- Check that debug code doesn't affect release builds
## Common Mistakes to Avoid
### Architecture Violations
```gdscript
# ❌ Don't bypass GameManager
get_tree().change_scene_to_file("some_scene.tscn")
# ❌ Don't hardcode paths
var tile = load("res://scenes/match3/tile.tscn")
# ❌ Don't ignore null checks
var node = get_node("SomeNode")
node.do_something() # Could crash if node doesn't exist
# ❌ Don't create global state in random scripts
# Use autoloads instead
```
### Performance Issues
```gdscript
# ❌ Don't search nodes repeatedly
func _process(delta):
var ui = get_node("UI") # Expensive every frame
# ✅ Cache node references
@onready var ui = $UI
func _process(delta):
ui.update_display() # Much better
```
### Debug System Misuse
```gdscript
# ❌ Don't create separate debug systems
var my_debug_enabled = false
# ✅ Use the global debug system
if DebugManager.is_debug_enabled():
show_debug_info()
```
## Learning Resources
### Godot-Specific
- [GDScript Style Guide](https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_styleguide.html)
- [Godot Best Practices](https://docs.godotengine.org/en/stable/tutorials/best_practices/index.html)
- Project documentation in `docs/map.md`
### General Programming
- Clean Code principles
- SOLID principles (adapted for game development)
- Git best practices
## Getting Help
### When Stuck
1. Check existing code for similar patterns
2. Review project documentation (`docs/`)
3. Use the debug system to understand current state
4. Ask specific questions about project architecture
5. Test your understanding with small experiments
### Code Review Process
- Submit pull requests early and often
- Ask for feedback on approach before implementing large features
- Be open to suggestions and alternative approaches
- Learn from review feedback for future contributions
## Summary
This code of conduct emphasizes:
- **Clarity**: Write code that others can understand
- **Consistency**: Follow established patterns
- **Integration**: Use the project's systems properly
- **Learning**: Continuously improve your skills
Remember: It's better to ask questions and write clear, simple code than to guess and create complex, hard-to-maintain solutions. The goal is to contribute effectively to a codebase that will grow and evolve over time.

209
docs/MAP.md Normal file
View File

@@ -0,0 +1,209 @@
# 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.
## Project Root Structure
```
skelly/
├── .claude/ # Claude Code configuration
├── .godot/ # Godot engine generated files (ignored)
├── assets/ # Game assets (sprites, audio, textures)
├── data/ # Game data and configuration files
├── docs/ # Documentation
├── localization/ # Internationalization files
├── scenes/ # Godot scenes (.tscn) and scripts (.gd)
├── src/ # Source code organization
├── project.godot # Main Godot project configuration
└── icon.svg # Project icon
```
## Core Architecture
### Autoloads (Global Singletons)
Located in `src/autoloads/`, these scripts are automatically loaded when the game starts:
1. **SettingsManager** (`src/autoloads/SettingsManager.gd`)
- Manages game settings and user preferences
- Handles configuration file I/O
- Provides language selection functionality
- Dependencies: `localization/languages.json`
2. **AudioManager** (`src/autoloads/AudioManager.gd`)
- Controls music and sound effects
- Manages audio bus configuration
- Uses: `data/default_bus_layout.tres`
3. **GameManager** (`src/autoloads/GameManager.gd`)
- Central game state management
- Scene transitions between main/game/match3
- Navigation flow control
- References: main.tscn, game.tscn, match3.tscn
4. **LocalizationManager** (`src/autoloads/LocalizationManager.gd`)
- Language switching functionality
- Works with Godot's built-in internationalization system
- Uses translation files in `localization/`
5. **DebugManager** (`src/autoloads/DebugManager.gd`)
- Global debug state management
- Debug UI visibility control
- F12 toggle functionality
- Signal-based debug system
## Scene Hierarchy & Flow
### Main Scenes
```
main.tscn (Entry Point)
├── PressAnyKeyScreen.tscn
├── MainMenu.tscn
└── SettingsMenu.tscn
```
### Game Flow
1. **Main Scene** (`scenes/main/main.tscn` + `Main.gd`)
- Application entry point
- Manages "Press Any Key" screen
- Transitions to main menu
- Dynamic menu loading system
2. **Press Any Key Screen** (`scenes/main/PressAnyKeyScreen.tscn` + `PressAnyKeyScreen.gd`)
- Initial splash screen
- Input detection for any key/button
- Signals to main scene for transition
3. **Main Menu** (`scenes/ui/MainMenu.tscn` + `MainMenu.gd`)
- Primary navigation hub
- Start game, settings, quit options
- Connected to GameManager for scene transitions
4. **Settings Menu** (`scenes/ui/SettingsMenu.tscn` + `SettingsMenu.gd`)
- User preferences interface
- Language selection
- Audio volume controls
- 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
### UI Components
```
scenes/ui/
├── DebugButton.tscn + DebugButton.gd
├── MainMenu.tscn + MainMenu.gd
└── SettingsMenu.tscn + SettingsMenu.gd
```
## Match-3 Game System
### Core Components
1. **Match3 Controller** (`scenes/match3/match3.gd`)
- Grid management (8x8 default)
- Match detection algorithms
- Tile dropping and refilling
- Gem pool management (3-8 gem types)
- Debug UI integration
2. **Tile System** (`scenes/match3/tile.gd` + `Tile.tscn`)
- Individual tile behavior
- Gem type management
- Visual representation
- Group membership for coordination
### 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
## Asset Organization
### Audio (`assets/audio/`)
```
audio/
├── music/ # Background music files
└── sfx/ # Sound effects
```
### Visual Assets (`assets/`)
```
assets/
├── sprites/
│ ├── characters/skeleton/ # Character artwork
│ ├── gems/ # Match-3 gem sprites
│ └── ui/ # User interface elements
├── textures/
│ └── backgrounds/ # Background images
└── fonts/ # Custom fonts
```
## Data & Configuration
### Game Data (`data/`)
- `default_bus_layout.tres` - Audio bus configuration for Godot
### Localization (`localization/`)
- `languages.json` - Available language definitions
- `MainStrings.en.translation` - English translations
- `MainStrings.ru.translation` - Russian translations
### Project Configuration
- `project.godot` - Main Godot project settings
- Autoload definitions
- Input map configurations
- Rendering settings
- Audio bus references
## Key Dependencies & Connections
### Signal Connections
```
PressAnyKeyScreen --[any_key_pressed]--> Main
MainMenu --[open_settings]--> Main
SettingsMenu --[back_to_main_menu]--> Main
DebugManager --[debug_toggled, debug_ui_toggled]--> Match3
```
### Scene References
```
GameManager --> main.tscn, game.tscn, match3.tscn
Main --> MainMenu.tscn, SettingsMenu.tscn
Game --> match3.tscn (instantiated)
```
### Asset Dependencies
```
AudioManager --> assets/audio/music/
SettingsManager --> localization/languages.json
AudioManager --> data/default_bus_layout.tres
```
## Development Notes
### Current Implementation Status
- Match-3 system with 8x8 grid and configurable gem pools
- Debug UI system with F12 toggle functionality
- Scene transition system via GameManager
- 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
This structure provides a clean separation of concerns, making the codebase maintainable and extensible for future features.

View File

@@ -25,6 +25,7 @@ SettingsManager="*res://src/autoloads/SettingsManager.gd"
AudioManager="*res://src/autoloads/AudioManager.gd"
GameManager="*res://src/autoloads/GameManager.gd"
LocalizationManager="*res://src/autoloads/LocalizationManager.gd"
DebugManager="*res://src/autoloads/DebugManager.gd"
[input]
@@ -55,4 +56,5 @@ locale/translations=PackedStringArray("res://localization/MainStrings.en.transla
[rendering]
textures/canvas_textures/default_texture_filter=0
renderer/rendering_method="mobile"
renderer/rendering_method="gl_compatibility"
renderer/rendering_method.mobile="gl_compatibility"

View File

@@ -1,27 +1,17 @@
extends Node
@onready var back_button: Button = $BackButtonContainer/BackButton
const MATCH3_SCENE_PATH := "res://scenes/match3/match3.tscn"
var match3_instance: Node
# Fixed: Use static Match3 instance from scene instead of dynamic loading
@onready var match3_instance: Node = $Match3
func _ready() -> void:
if not back_button.pressed.is_connected(_on_back_button_pressed):
back_button.pressed.connect(_on_back_button_pressed)
_load_match3_scene()
# Fixed: No need to load match3 scene - it's already in the scene tree
func _on_back_button_pressed() -> void:
AudioManager.play_ui_click()
GameManager.save_game()
if match3_instance and is_instance_valid(match3_instance):
match3_instance.queue_free()
# Fixed: Don't free the static instance, just exit
GameManager.exit_to_main_menu()
func _load_match3_scene() -> void:
var match3_scene := load(MATCH3_SCENE_PATH)
if not match3_scene or not match3_scene is PackedScene:
push_error("Failed to load Match3 scene at: %s" % MATCH3_SCENE_PATH)
return
match3_instance = match3_scene.instantiate()
add_child(match3_instance)

View File

@@ -1,7 +1,9 @@
[gd_scene load_steps=3 format=3 uid="uid://dmwkyeq2l7u04"]
[gd_scene load_steps=5 format=3 uid="uid://dmwkyeq2l7u04"]
[ext_resource type="Script" uid="uid://bvtr6yhlyuv4v" path="res://scenes/game/game.gd" id="1_uwrxv"]
[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"]
[node name="Game" type="Node"]
script = ExtResource("1_uwrxv")
@@ -19,3 +21,7 @@ offset_bottom = 31.0
text = "back"
[node name="Match3" parent="." instance=ExtResource("2_yqjtg")]
[node name="DebugToggle" parent="." instance=ExtResource("3_debug")]
[node name="DebugMenu" parent="." instance=ExtResource("4_debug_menu")]

View File

@@ -1,6 +1,6 @@
[gd_scene load_steps=15 format=3 uid="uid://gbe1jarrwqsi"]
[ext_resource type="Script" uid="uid://buak21ajgvevl" path="res://scenes/main/PressAnyKeyScreen.gd" id="1_0a4p2"]
[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"]
[sub_resource type="AtlasTexture" id="AtlasTexture_l6pue"]

View File

@@ -1,8 +1,9 @@
[gd_scene load_steps=4 format=3 uid="uid://ci2gk11211n0d"]
[gd_scene load_steps=5 format=3 uid="uid://ci2gk11211n0d"]
[ext_resource type="Script" uid="uid://cwlop1ettlqhg" path="res://scenes/main/Main.gd" id="1_0wfyh"]
[ext_resource type="Script" uid="uid://b0uwk2jlm6g08" path="res://scenes/main/Main.gd" id="1_0wfyh"]
[ext_resource type="PackedScene" uid="uid://gbe1jarrwqsi" path="res://scenes/main/PressAnyKeyScreen.tscn" id="1_o5qli"]
[ext_resource type="Texture2D" uid="uid://c8y6tlvcgh2gn" path="res://assets/textures/backgrounds/beanstalk-dark.webp" id="2_sugp2"]
[ext_resource type="PackedScene" path="res://scenes/ui/DebugToggle.tscn" id="4_v7g8d"]
[node name="main" type="Control"]
layout_mode = 3
@@ -25,3 +26,6 @@ stretch_mode = 1
[node name="PressAnyKeyScreen" parent="." instance=ExtResource("1_o5qli")]
layout_mode = 1
[node name="DebugToggle" parent="." instance=ExtResource("4_v7g8d")]
layout_mode = 1

View File

@@ -1,26 +1,71 @@
extends Node2D
const GRID_SIZE := Vector2i(8, 8)
const TILE_TYPES := 5
var GRID_SIZE := Vector2i(8, 8)
var TILE_TYPES := 5
const TILE_SCENE := preload("res://scenes/match3/tile.tscn")
var grid := []
var tile_size: float = 48.0
var grid_offset: Vector2
func _ready():
# Set up initial gem pool
var gem_indices = []
for i in range(TILE_TYPES):
gem_indices.append(i)
const TileScript = preload("res://scenes/match3/tile.gd")
TileScript.set_active_gem_pool(gem_indices)
_calculate_grid_layout()
_initialize_grid()
func _calculate_grid_layout():
var viewport_size = get_viewport().get_visible_rect().size
var available_width = viewport_size.x * 0.8 # Use 80% of screen width
var available_height = viewport_size.y * 0.7 # Use 70% of screen height
# Calculate tile size based on available space
var max_tile_width = available_width / GRID_SIZE.x
var max_tile_height = available_height / GRID_SIZE.y
tile_size = min(max_tile_width, max_tile_height)
# Align grid to left side with some margin
var total_grid_height = tile_size * GRID_SIZE.y
grid_offset = Vector2(
50, # Left margin
(viewport_size.y - total_grid_height) / 2 + 50 # Vertically centered with top margin
)
func _initialize_grid():
# Update gem pool BEFORE creating any tiles
var gem_indices = []
for i in range(TILE_TYPES):
gem_indices.append(i)
const TileScript = preload("res://scenes/match3/tile.gd")
TileScript.set_active_gem_pool(gem_indices)
for y in range(GRID_SIZE.y):
grid.append([])
for x in range(GRID_SIZE.x):
var tile = TILE_SCENE.instantiate()
tile.position = Vector2(x, y) * 64 # Adjust to your tile size
tile.position = grid_offset + Vector2(x, y) * tile_size
tile.grid_position = Vector2i(x, y)
tile.tile_type = randi() % TILE_TYPES
add_child(tile)
# 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)
grid[y].append(tile)
func _has_match_at(pos: Vector2i) -> bool:
# Fixed: Add bounds and null checks
if pos.x < 0 or pos.y < 0 or pos.x >= GRID_SIZE.x or pos.y >= GRID_SIZE.y:
return false
if not grid[pos.y][pos.x]:
return false
var matches_horizontal = _get_match_line(pos, Vector2i(1, 0))
if matches_horizontal.size() >= 3:
return true
@@ -28,15 +73,25 @@ func _has_match_at(pos: Vector2i) -> bool:
var matches_vertical = _get_match_line(pos, Vector2i(0, 1))
return matches_vertical.size() >= 3
# Fixed: Add missing function to check for any matches on the board
func _check_for_matches() -> bool:
for y in range(GRID_SIZE.y):
for x in range(GRID_SIZE.x):
if _has_match_at(Vector2i(x, y)):
return true
return false
func _get_match_line(start: Vector2i, dir: Vector2i) -> Array:
var line = [grid[start.y][start.x]]
var type = grid[start.y][start.x].tile_type
# Fixed: Check in both directions separately to avoid infinite loops
for offset in [1, -1]:
var current = start + dir * offset
while current.x >= 0 and current.y >= 0 and current.x < GRID_SIZE.x and current.y < GRID_SIZE.y:
var neighbor = grid[current.y][current.x]
if neighbor.tile_type != type:
# Fixed: Add null check to prevent crashes
if not neighbor or neighbor.tile_type != type:
break
line.append(neighbor)
current += dir * offset
@@ -48,6 +103,9 @@ func _clear_matches():
for y in range(GRID_SIZE.y):
for x in range(GRID_SIZE.x):
# Fixed: Add null check before processing
if not grid[y][x]:
continue
var pos = Vector2i(x, y)
var horiz = _get_match_line(pos, Vector2i(1, 0))
if horiz.size() >= 3:
@@ -62,6 +120,10 @@ func _clear_matches():
unique_dict[tile] = true
to_clear = unique_dict.keys()
# Fixed: Only proceed if there are matches to clear
if to_clear.size() == 0:
return
for tile in to_clear:
grid[tile.grid_position.y][tile.grid_position.x] = null
tile.queue_free()
@@ -75,15 +137,17 @@ func _drop_tiles():
while moved:
moved = false
for x in range(GRID_SIZE.x):
for y in range(GRID_SIZE.y - 2, -1, -1):
# Fixed: Start from GRID_SIZE.y - 1 to avoid out of bounds
for y in range(GRID_SIZE.y - 1, -1, -1):
var tile = grid[y][x]
if tile and not grid[y + 1][x]:
# Fixed: Check bounds before accessing y + 1
if tile and y + 1 < GRID_SIZE.y and not grid[y + 1][x]:
grid[y + 1][x] = tile
grid[y][x] = null
tile.grid_position = Vector2i(x, y + 1)
# You can animate position here using Tween for smooth drop:
# tween.interpolate_property(tile, "position", tile.position, Vector2(x, y + 1) * 64, 0.2)
tile.position = Vector2(x, y + 1) * 64
# tween.interpolate_property(tile, "position", tile.position, grid_offset + Vector2(x, y + 1) * tile_size, 0.2)
tile.position = grid_offset + Vector2(x, y + 1) * tile_size
moved = true
func _fill_empty_cells():
@@ -93,10 +157,63 @@ func _fill_empty_cells():
var tile = TILE_SCENE.instantiate()
tile.grid_position = Vector2i(x, y)
tile.tile_type = randi() % TILE_TYPES
tile.position = Vector2(x, y) * 64
tile.position = grid_offset + Vector2(x, y) * tile_size
grid[y][x] = tile
add_child(tile)
# Recheck matches
# Fixed: Add recursion protection to prevent stack overflow
await get_tree().create_timer(0.1).timeout
_clear_matches()
var max_iterations = 10
var iteration = 0
while _check_for_matches() and iteration < max_iterations:
_clear_matches()
await get_tree().create_timer(0.1).timeout
iteration += 1
func regenerate_grid():
# Use time-based seed to ensure different patterns each time
var new_seed = Time.get_ticks_msec()
seed(new_seed)
print("Debug: Regenerating grid with seed: ", new_seed)
# Fixed: Clear ALL tile children, not just ones in grid array
# This handles any orphaned tiles that might not be tracked in the grid
var children_to_remove = []
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":
children_to_remove.append(child)
# Remove all found tile children
for child in children_to_remove:
child.free()
# Also clear grid array references
for y in range(grid.size()):
if grid[y] and grid[y] is Array:
for x in range(grid[y].size()):
# Set to null since we already freed the nodes above
grid[y][x] = null
# Clear the grid array
grid.clear()
# No need to wait for nodes to be freed since we used free()
# Fixed: Recalculate grid layout before regenerating tiles
_calculate_grid_layout()
# Regenerate the grid (gem pool is updated in _initialize_grid)
_initialize_grid()
func set_tile_types(new_count: int):
TILE_TYPES = new_count
# Regenerate grid with new tile types (gem pool is updated in regenerate_grid)
await regenerate_grid()
func set_grid_size(new_size: Vector2i):
print("Debug: Changing grid size from ", GRID_SIZE, " to ", new_size)
GRID_SIZE = new_size
# Regenerate grid with new size
await regenerate_grid()

View File

@@ -1,13 +1,8 @@
[gd_scene load_steps=3 format=3 uid="uid://b4kv7g7kllwgb"]
[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"]
[ext_resource type="Texture2D" uid="uid://ccprr0qrj3lgm" path="res://assets/sprites/gems/rg_19.png" id="2_0fhn8"]
[node name="Match3" type="Node2D"]
script = ExtResource("1_mvfdp")
[node name="GridContainer" type="Node2D" parent="."]
[node name="Sprite2D" type="Sprite2D" parent="."]
position = Vector2(584, 317)
texture = ExtResource("2_0fhn8")

View File

@@ -1,33 +1,116 @@
extends Node2D
@export var tile_type: int = 0
@export var tile_type: int = 0 : set = _set_tile_type
var grid_position: Vector2i
@onready var sprite: Sprite2D = $Sprite2D
# Target size for each tile to fit in the 64x64 grid cells
const TILE_SIZE = 48 # Slightly smaller than 64 to leave some padding
# Target size for each tile to fit in the 54x54 grid cells
const TILE_SIZE = 48 # Slightly smaller than 54 to leave some padding
var tile_textures = [
preload("res://assets/sprites/gems/bg_19.png"),
preload("res://assets/sprites/gems/dg_19.png"),
preload("res://assets/sprites/gems/gg_19.png"),
preload("res://assets/sprites/gems/mg_19.png"),
# All available gem textures
var all_gem_textures = [
preload("res://assets/sprites/gems/bg_19.png"), # 0 - Blue gem
preload("res://assets/sprites/gems/dg_19.png"), # 1 - Dark gem
preload("res://assets/sprites/gems/gg_19.png"), # 2 - Green gem
preload("res://assets/sprites/gems/mg_19.png"), # 3 - Magenta gem
preload("res://assets/sprites/gems/rg_19.png"), # 4 - Red gem
preload("res://assets/sprites/gems/yg_19.png"), # 5 - Yellow gem
preload("res://assets/sprites/gems/pg_19.png"), # 6 - Purple gem
preload("res://assets/sprites/gems/sg_19.png"), # 7 - Silver gem
]
# Static variable to store the current gem pool for all tiles
static var current_gem_pool = [0, 1, 2, 3, 4] # Start with first 5 gems
# Currently active gem types (indices into all_gem_textures)
var active_gem_types = [] # Will be set from current_gem_pool
func _set_tile_type(value: int) -> void:
tile_type = value
if value >= 0 and value < tile_textures.size():
sprite.texture = tile_textures[value]
print("Debug: _set_tile_type called with value ", value, ", active_gem_types.size() = ", active_gem_types.size())
# Fixed: Add sprite null check to prevent crashes during initialization
if not sprite:
return
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)
_scale_sprite_to_fit()
else:
push_error("Invalid tile type: " + str(value) + ". Available types: 0-" + str(active_gem_types.size() - 1))
func _scale_sprite_to_fit() -> void:
if sprite.texture:
# Fixed: Add additional null checks
if sprite and sprite.texture:
var texture_size = sprite.texture.get_size()
var max_dimension = max(texture_size.x, texture_size.y)
var scale_factor = TILE_SIZE / max_dimension
sprite.scale = Vector2(scale_factor, scale_factor)
# Gem pool management functions
static func set_active_gem_pool(gem_indices: Array) -> void:
# Update static gem pool for new tiles
current_gem_pool = gem_indices.duplicate()
# Update all existing tile instances to use new gem pool
var scene_tree = Engine.get_main_loop() as SceneTree
if scene_tree:
var tiles = scene_tree.get_nodes_in_group("tiles")
for tile in tiles:
if tile.has_method("_update_active_gems"):
tile._update_active_gems(gem_indices)
func _update_active_gems(gem_indices: Array) -> void:
active_gem_types = gem_indices.duplicate()
# Re-validate current tile type
if tile_type >= active_gem_types.size():
# Generate a new random tile type within valid range
tile_type = randi() % active_gem_types.size()
_set_tile_type(tile_type)
static func get_active_gem_count() -> int:
# Get from any tile instance or default
var scene_tree = Engine.get_main_loop() as SceneTree
if scene_tree:
var tiles = scene_tree.get_nodes_in_group("tiles")
if tiles.size() > 0:
return tiles[0].active_gem_types.size()
return 5 # Default
static func add_gem_to_pool(gem_index: int) -> void:
var scene_tree = Engine.get_main_loop() as SceneTree
if scene_tree:
var tiles = scene_tree.get_nodes_in_group("tiles")
for tile in tiles:
if tile.has_method("_add_gem_type"):
tile._add_gem_type(gem_index)
func _add_gem_type(gem_index: int) -> void:
if gem_index >= 0 and gem_index < all_gem_textures.size():
if not active_gem_types.has(gem_index):
active_gem_types.append(gem_index)
static func remove_gem_from_pool(gem_index: int) -> void:
var scene_tree = Engine.get_main_loop() as SceneTree
if scene_tree:
var tiles = scene_tree.get_nodes_in_group("tiles")
for tile in tiles:
if tile.has_method("_remove_gem_type"):
tile._remove_gem_type(gem_index)
func _remove_gem_type(gem_index: int) -> void:
var type_index = active_gem_types.find(gem_index)
if type_index != -1 and active_gem_types.size() > 2: # Keep at least 2 gem types
active_gem_types.erase(gem_index)
# Update tiles that were using removed type
if tile_type >= active_gem_types.size():
tile_type = 0
_set_tile_type(tile_type)
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
add_to_group("tiles") # Add to group for gem pool management
# Initialize with current static gem pool
active_gem_types = current_gem_pool.duplicate()
_set_tile_type(tile_type)

View File

@@ -0,0 +1,25 @@
[gd_scene load_steps=2 format=3 uid="uid://dmjfgs6r4yfwl"]
[ext_resource type="Script" uid="uid://bt6a8gpw1e31c" path="res://scenes/ui/DebugButton.gd" id="1_debug_script"]
[node name="DebugButton" type="Control"]
layout_mode = 3
anchors_preset = 3
anchor_left = 1.0
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = -80.0
offset_top = -40.0
grow_horizontal = 0
grow_vertical = 0
script = ExtResource("1_debug_script")
[node name="Button" type="Button" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
text = "DEBUG"

203
scenes/ui/DebugMenu.gd Normal file
View File

@@ -0,0 +1,203 @@
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
var search_timer: Timer
# Fixed: Add cleanup function
func _exit_tree():
if search_timer:
search_timer.queue_free()
func _ready():
visible = false
DebugManager.debug_toggled.connect(_on_debug_toggled)
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)
# Initialize gem types spinbox
gem_types_spinbox.min_value = 3
gem_types_spinbox.max_value = 8
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
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.step = 1
grid_height_spinbox.value = 8 # Default value
# Fixed: Create timer for periodic match3 scene search
search_timer = Timer.new()
search_timer.wait_time = 0.1
search_timer.timeout.connect(_find_match3_scene)
add_child(search_timer)
# Start searching immediately and continue until found
_find_match3_scene()
func _find_match3_scene():
# Fixed: Search more thoroughly for match3 scene
if match3_scene:
# Already found, stop searching
if search_timer and search_timer.timeout.is_connected(_find_match3_scene):
search_timer.stop()
return
# Search in current scene tree
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")
# Fallback: search by common node names
if not match3_scene:
for possible_name in ["Match3", "match3", "Match3Game"]:
match3_scene = current_scene.find_child(possible_name, true, false)
if match3_scene:
break
if match3_scene:
print("Debug: Found match3 scene: ", match3_scene.name)
# Update UI with current values
if 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 values
if "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)
# Stop the search timer
if search_timer and search_timer.timeout.is_connected(_find_match3_scene):
search_timer.stop()
else:
# Continue searching if not found
if search_timer and not search_timer.timeout.is_connected(_find_match3_scene):
search_timer.timeout.connect(_find_match3_scene)
search_timer.start()
func _find_node_by_script(node: Node, script_path: String) -> Node:
# Helper function to find node by its script path
if node.get_script():
var node_script = node.get_script()
if node_script.resource_path == script_path:
return node
for child in node.get_children():
var result = _find_node_by_script(child, script_path)
if result:
return result
return null
func _on_debug_toggled(enabled: bool):
visible = enabled
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")
# Fallback: try to set TILE_TYPES directly
if "TILE_TYPES" in match3_scene:
match3_scene.TILE_TYPES = new_value
gem_types_label.text = "Gem Types: " + str(new_value)
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))
else:
print("Error: match3_scene does not have set_grid_size method")
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))
else:
print("Error: match3_scene does not have set_grid_size method")

83
scenes/ui/DebugMenu.tscn Normal file
View File

@@ -0,0 +1,83 @@
[gd_scene load_steps=2 format=3 uid="uid://fax5m4ywp1r3"]
[ext_resource type="Script" uid="uid://b4ppfg1tb0al0" path="res://scenes/ui/DebugMenu.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 = "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

12
scenes/ui/DebugToggle.gd Normal file
View File

@@ -0,0 +1,12 @@
extends Button
func _ready():
text = "Debug: OFF"
pressed.connect(_on_pressed)
DebugManager.debug_toggled.connect(_on_debug_toggled)
func _on_pressed():
DebugManager.toggle_debug()
func _on_debug_toggled(enabled: bool):
text = "Debug: " + ("ON" if enabled else "OFF")

View File

@@ -0,0 +1,18 @@
[gd_scene load_steps=2 format=3 uid="uid://df2b4wn8j6cxl"]
[ext_resource type="Script" path="res://scenes/ui/DebugToggle.gd" id="1_gn2ol"]
[node name="DebugToggle" type="Button"]
anchors_preset = 3
anchor_left = 1.0
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = -110.0
offset_top = -40.0
offset_right = -10.0
offset_bottom = -10.0
grow_horizontal = 0
grow_vertical = 0
text = "Debug: OFF"
script = ExtResource("1_gn2ol")

View File

@@ -1,6 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://m8lf3eh3al5j"]
[ext_resource type="Script" uid="uid://ctu58xq7btp1n" path="res://scenes/ui/MainMenu.gd" id="1_b00nv"]
[ext_resource type="Script" uid="uid://b2c35v0f6rymd" path="res://scenes/ui/MainMenu.gd" id="1_b00nv"]
[node name="MainMenu" type="Control"]
layout_mode = 3

View File

@@ -1,6 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://57obmcwyos2g"]
[ext_resource type="Script" uid="uid://c8mlvlerv4y03" path="res://scenes/ui/SettingsMenu.gd" id="1_oqkcn"]
[ext_resource type="Script" uid="uid://bv56qwni68qo" path="res://scenes/ui/SettingsMenu.gd" id="1_oqkcn"]
[node name="SettingsMenu" type="Control" groups=["localizable"]]
layout_mode = 3

View File

@@ -0,0 +1,35 @@
extends Node
signal debug_toggled(enabled: bool)
var debug_enabled: bool = false
var debug_overlay_visible: bool = false
func _ready():
print("DebugManager loaded")
func toggle_debug():
debug_enabled = !debug_enabled
debug_toggled.emit(debug_enabled)
print("Debug mode: ", "ON" if debug_enabled else "OFF")
func set_debug_enabled(enabled: bool):
if debug_enabled != enabled:
debug_enabled = enabled
debug_toggled.emit(debug_enabled)
func is_debug_enabled() -> bool:
return debug_enabled
func toggle_overlay():
debug_overlay_visible = !debug_overlay_visible
func set_overlay_visible(visible: bool):
debug_overlay_visible = visible
func is_overlay_visible() -> bool:
return debug_overlay_visible
func log_debug(message: String, category: String = "DEBUG"):
if debug_enabled:
print("[", category, "] ", message)