Author SHA1 Message Date
Andrew Yeet 928681a64a [PR Helper] Add Gitea API PR creation helper and make-pr skill
Validate Project / validate (push) Successful in 15s
Validate Project / validate (pull_request) Successful in 14s
2026-09-04 11:14:08 -07:00
14 changed files with 1498 additions and 2652 deletions
-1
View File
@@ -17,4 +17,3 @@ osu!/
.env .env
.env.gitea .env.gitea
*.uid
+1 -1
View File
@@ -36,4 +36,4 @@
"get_diff_summary": "git diff --name-only ${TARGET_BRANCH:-develop}..HEAD 2>/dev/null | wc -l || echo \"0\"", "get_diff_summary": "git diff --name-only ${TARGET_BRANCH:-develop}..HEAD 2>/dev/null | wc -l || echo \"0\"",
"validate_git_status": "git status --short && git log -1 --oneline --quiet" "validate_git_status": "git status --short && git log -1 --oneline --quiet"
} }
} }
-455
View File
@@ -1,455 +0,0 @@
# Testing Workflow Guide for YeetGeese
This document provides a structured approach to testing Godot 4.x games, with specific focus on the tower defense mechanics of YeetGeese. It covers what to test, how to structure tests, and debugging failed tests.
---
## Table of Contents
1. [Testing Philosophy](#testing-philosophy)
2. [Test Organization](#test-organization)
3. [Test Categories](#test-categories)
4. [Writing Tests](#writing-tests)
5. [Debugging Failed Tests](#debugging-failed-tests)
6. [CI Integration](#ci-integration)
---
## Testing Philosophy
### Why Test?
- **Regression prevention:** Catch unintended side effects from changes
- **Documentation by example:** Tests serve as usage examples
- **Design validation:** Ensure architecture matches intent
- **Confidence for refactoring:** Safe to reorganize code when tested
### What AI Can Help With
AI can:
- Generate boilerplate test scaffolding
- Suggest edge cases and boundary conditions
- Write mock implementations for complex systems
- Explain Godot testing framework capabilities
### What AI Cannot Handle
AI should NOT:
- Design game mechanics or balance (human domain)
- Make subjective quality-of-life judgments
- Replace manual playtesting of actual gameplay feel
- Generate test data for art assets (no AI-generated art rule applies)
---
## Test Organization
### Directory Structure
```
tests/
├── core/ # Autoloads and global state tests
│ ├── wave_progression_test.gd
│ └── currency_manager_test.gd
├── towers/ # Tower behavior tests
│ ├── tower_base_test.gd
│ ├── projectile_spawner_test.gd
│ └── target_finder_test.gd
├── enemies/ # Enemy system tests
│ ├── enemy_mob_test.gd
│ ├── navigation_system_test.gd
│ └── spawner_test.gd
├── projectiles/ # Projectile handling tests
│ ├── goose_projectile_test.gd
│ └── impact_handler_test.gd
├── ui/ # HUD and menu tests
│ ├── health_bar_test.gd
│ └── currency_display_test.gd
├── utils/ # Utility functions
│ └── math_utils_test.gd
└── integration/ # Cross-system tests
├── wave_complete_sequence_test.gd
└── tower_placement_integration_test.gd
```
### Naming Conventions
- Files: `<system>_test.gd` (e.g., `tower_base_test.gd`)
- Functions: `func test_<behavior_description>()` (e.g., `func test_fire_rate_scales_with_level()`)
- Assertions: Use descriptive names like `assert_equal`, `assert_signal_emitted`, `assert_no_error`
---
## Test Categories
### Unit Tests
Test individual functions or methods in isolation.
**Example:** Testing a tower's fire rate calculation
```gdscript
@tool
extends Node
func test_fire_rate_calculation(tower: Node) -> void:
assert_equal(tower.fire_rate, 1.0)
# Modify tower level
tower.tower_level = 2
# Verify fire rate changed appropriately
assert_greater(tower.fire_rate, 1.0)
assert_less(tower.fire_rate, 2.0)
func test_damage_scaling_by_tower_level(tower: Node) -> void:
tower.tower_level = 3
var initial_damage = tower.get_damage_at_current_level()
tower.tower_level = 5
# Verify damage increased with level
assert_greater(tower.get_damage_at_current_level(), initial_damage)
```
**When to use:**
- Testing pure logic (math, state transitions)
- Testing Resource loading and property access
- Testing signal emission patterns
---
### Integration Tests
Test interactions between multiple systems.
**Example:** Testing the full projectile launch sequence
```gdscript
@tool
extends Node
func test_projectile_launch_sequence() -> void:
# Arrange: Create mock tower and target
var tower := _create_mock_tower()
var target := _create_mock_target()
# Act: Fire at target
tower.fire_at(target)
# Assert: Verify projectile spawned in correct state
assert_true(tower.projectile_count == 1)
assert_equal(tower.projectiles[0].source_node, tower.$ProjectileSpawner)
```
**When to use:**
- Testing scene hierarchies with multiple nodes
- Verifying signal connections between systems
- Validating Godot's built-in component behavior (e.g., RigidBody2D movement)
---
### Regression Tests
Tests added when a bug is fixed to ensure it doesn't return.
**Example:** Preventing physics tunneling after a collision fix
```gdscript
@tool
extends Node
func test_projectile_collision_prevents_tunneling() -> void:
# Scenario: Goose projectile moving at high speed
var projectile := _create_goose_with_velocity(100.0)
var enemy := _create_enemy_with_small_hitbox()
# Act: Move projectile toward enemy's hitbox
projectile.yeet_from(projectile.position, enemy.position)
# Assert: Projectile detected collision on this frame
assert_signal_emitted(projectile, 'collision_detected')
```
**When to use:**
- After fixing a critical bug
- When performance regression occurs
- To validate edge case handling
---
## Writing Tests
### Test Function Template
```gdscript
@tool
extends Node
func test_<behavior_under_test>() -> void:
# Arrange: Setup the scenario
var <subject> := _create_mock_<subject>()
# Act: Execute the action being tested
<subject>.<method_or_event>()
# Assert: Verify expected behavior
assert_true(<condition_1>)
assert_false(<condition_2>)
assert_signal_emitted(<signal_source>, '<signal_name>')
```
### Available Assertions
| Assertion | Purpose | Example |
|-----------|---------|---------|
| `assert_equal(a, b)` | Check exact equality | `assert_equal(tower.level, 3)` |
| `assert_not_equal(a, b)` | Check inequality | `assert_not_equal(projectile.speed, 0.0)` |
| `assert_true(condition)` | Verify boolean is true | `assert_true(tower.is_ready())` |
| `assert_false(condition)` | Verify boolean is false | `assert_false(projectile.is_removed())` |
| `assert_greater(a, b)` | Check a > b | `assert_greater(damage_dealt, 10)` |
| `assert_less(a, b)` | Check a < b | `assert_less(enemy.health, 50.0)` |
| `assert_null(obj)` | Verify object is null | `assert_null(projectile.get_target())` |
| `assert_not_null(obj)` | Verify object exists | `assert_not_null(projectile)` |
| `assert_array_size(arr, size)` | Check array length | `assert_array_size(tower.projectiles, 4)` |
### Signal Testing Pattern
```gdscript
var _signal_verified = false
func test_health_changed_signal_emits_correct_values() -> void:
var enemy := EnemyMob.new()
enemy.health_changed.connect(func(new_health) {
assert_true(_signal_verified == false) # Prevent double-firing
assert_equal(new_health, 75.0)
_signal_verified = true
})
enemy.take_damage(25.0)
assert_true(_signal_verified)
```
### Mock Object Helpers
Use these helper functions to create mock objects for tests:
```gdscript
# Create a mock tower with minimal setup
func _create_mock_tower() -> Node:
var tower := TowerBase.new()
tower.tower_level = 1
return tower
# Create a mock enemy for collision testing
func _create_mock_enemy() -> EnemyMob:
var enemy := EnemyMob.new()
enemy.health = 50.0
enemy.damage_taken.connect(func() { pass }) # Prevent death on damage
return enemy
# Verify no error occurred during test
func _assert_no_error(condition: bool) -> void:
assert_true(condition, "Test completed with errors")
```
---
## Debugging Failed Tests
### Common Failure Patterns
#### 1. Test Completes Before Signal Fires
**Problem:** `signal_emitted` verification fails because the test ends too early.
**Solution:** Add a yield or use Godot's built-in coroutine system:
```gdscript
func test_signal_fires_after_delay() -> void:
var subject := _create_subject()
subject.event.triggered.connect(_on_event_triggered)
subject.do_something()
# Ensure signal fires before test ends
get_tree().process_frame.connect(_on_frame_processed)
yield(get_tree(), "process_frame") # Wait for signal
assert_true(_signal_received)
func _on_frame_processed() -> void:
_on_event_triggered.call_deferred()
```
#### 2. Scene Loading in Tests Fails
**Problem:** Godot scenes loaded via `load("res://...")` may not find their resources.
**Solution:** Use absolute paths or ensure test assets are in proper location:
```gdscript
# Bad: Relative path that breaks in tests
var mob_scene := load("enemy_mob.tscn").instantiate()
# Good: Absolute resource path
var mob_scene := load("res://src/enemies/enemy_mob.tscn").instantiate()
```
#### 3. Random Behavior Causes Flaky Tests
**Problem:** Tests pass sometimes but fail others due to randomness (e.g., particle emission).
**Solution:** Disable randomness in test environment:
```gdscript
# At start of test, seed RNG for reproducibility
Rand.randi = func() -> int: return 42 # Fixed value for testing
# Or disable randomness for specific systems
var projectile := Projectile.new()
projectile.random_variation_enabled = false
```
#### 4. Physics Tunneling in Collision Tests
**Problem:** Fast-moving projectiles miss collisions when tested with static enemies.
**Solution:** Use multiple physics frames or smaller time steps:
```gdscript
func test_high_velocity_collision_detection() -> void:
var projectile := _create_goose_with_velocity(200.0)
# Run multiple physics frames for collision detection
for _ in range(5):
get_tree().physics_ticks_processed = 1
get_tree().process_frame()
assert_true(_collision_detected)
```
#### 5. Signal Connections Leak Between Tests
**Problem:** Test A's signal handlers remain active when Test B runs.
**Solution:** Disconnect signals at test end:
```gdscript
func cleanup_test(subject: Node, signal_name: String) -> void:
subject.signal_removed.connect(_disconnect_signals)
# Clear all signals from subject
var signals = subject.get_signal_list()
for s in signals:
if s.is_connected(func):
s.disconnect(func)
# Call cleanup at end of each test function
```
---
## CI Integration (Optional for Indie Teams)
### GitHub Actions / GitLab CI Setup
If using a continuous integration server, add tests to build pipeline.
**Example workflow:**
```yaml
name: YeetGeese Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Godot
run: |
# Download and install Godot 4.x stable
- name: Run Tests
run: |
godot --path . --headless tests/run_all_tests.gd
```
### Local Test Runner Script
Create a script to run all tests from command line:
```gdscript
# tests/run_all_tests.gd
extends Node
func _ready():
var result := 0
# Run unit tests
for file in DirAccess.get_directories() if dir_name == "core" or dir_name == "towers":
pass # Implementation depends on test runner framework
if result == 0:
print("All tests passed!")
else:
error("Some tests failed")
func _exit_tree():
quit(0)
```
---
## Testing Checklist
### Before Writing Tests for New Feature
- [ ] Identify the system(s) being tested
- [ ] Determine unit vs. integration testing needs
- [ ] List expected inputs and outputs
- [ ] Identify edge cases (empty arrays, null objects, etc.)
### Test Coverage Goals
| System Type | Recommended Test Coverage |
|-------------|---------------------------|
| Core game loop | 100% (critical path) |
| Tower logic | 90%+ |
| Enemy AI states | 85%+ |
| UI displays | 80%+ |
| Utility functions | 95%+ |
### Review Checklist for AI-Generated Tests
When asking AI to generate tests, verify:
- [ ] Uses `@tool` directive for editor-only execution
- [ ] Sets up proper mock objects for isolation
- [ ] Disconnects signals at test end to prevent leaks
- [ ] Uses absolute paths for resource loading
- [ ] Handles expected error conditions gracefully
- [ ] Includes assertions for all critical behaviors
---
## Summary
**AI can help with:**
- Generating boilerplate test scaffolding
- Suggesting edge cases and boundary conditions
- Writing mock implementations for complex systems
- Explaining Godot testing framework capabilities
**Remember:**
- Tests should be runnable in editor (`@tool` functions)
- Keep tests focused on one behavior per function
- Disconnect signals to prevent cross-test contamination
- Use absolute paths for resource loading in tests
- Document test intent with clear, descriptive names
For more details, see `AI_HELP.md` for prompt templates when working with AI assistants on testing tasks.
-547
View File
@@ -1,547 +0,0 @@
# Skills, Agents, and Commands Reference
This document provides a reference for AI assistants working with YeetGeese. It describes available capabilities, when to use them, and best practices for interaction.
---
## Table of Contents
1. [Available Skills](#available-skills)
2. [Agent Types & Use Cases](#agent-types--use-cases)
3. [Command Reference](#command-reference)
4. [Prompt Templates](#prompt-templates)
5. [Best Practices](#best-practices)
---
## Available Skills
### `customize-opencode`
**Purpose:** Configure and modify the AI assistant's own settings, including:
- `opencode.json`, `opencode.jsonc` files
- `.opencode/` configuration directory
- `~/.config/opencode/` user settings
- Agent definitions (subagents, skills, plugins, MCP servers)
- Permission rules
**When to use:** When configuring the AI assistant itself, not for project code.
**Example:**
```
"Adjust the maximum context window size for this session."
```
---
### `make-pr` — Pull Request Creation (Gitea Web UI)
**Purpose:** On Gitea, pull requests are created directly via the web interface at:
`https://gitea.letteka.com/letteka/YeetGeese/pulls/new/move_docs`
**How to use on Gitea:**
1. Push changes to a branch (e.g., `move_docs`)
2. Visit the URL above or navigate via Gitea web UI
3. Create PR with descriptive title and body
4. Include testing strategies and validation status in description
**When to use:**
- When ready to submit a pull request from your current branch
- Before pushing code that requires peer review
- To ensure all changes are properly documented and validated
- When creating PRs for features, fixes, or refactoring
**Best practices for Gitea PRs:**
- Use descriptive titles following conventional commits format
- Include file-by-file change summary in PR body
- List testing strategies: unit tests, integration tests, manual QA
- Mention any validation already performed locally
- Flag known issues or limitations clearly
---
## Agent Types & Use Cases
### `code-gen` — Code Generation
**Purpose:** Write new code, implement features, create boilerplate.
**Capabilities:**
- GDScript 4 implementation
- Scene graph construction descriptions
- Resource file (`.tres`) schema design
- Signal declarations and connections
- Method implementations matching project conventions
**When to use:**
- Implementing a new tower or projectile type
- Adding a feature to existing systems
- Creating boilerplate for common patterns
- Writing unit tests
**Input required:**
- Target file path or node description
- Desired behavior/goal
- Any constraints (e.g., "must use Resource for stats")
**Example prompt:**
```
"Create a GDScript 4 class `ImpactEffect.gd` that extends Area2D. It should:
1. Detect collision with enemies in _body_entered
2. Emit signal 'effect_triggered' with damage and target Node2D
3. Apply random color variation to its texture
4. Set lifespan to 0.8 seconds
5. Include comments explaining each step"
```
---
### `debug-helper` — Debugging Assistance
**Purpose:** Analyze errors, suggest debugging strategies, interpret profiler output.
**Capabilities:**
- Error message interpretation (Godot/IDE/compiler)
- Suggesting debugging tool usage (Scene Debugger, Physics Monitor)
- Profiling tips and bottleneck identification
- Common pitfall recognition (e.g., physics tunneling, navmesh gaps)
**When to use:**
- When an error occurs during implementation
- When code behaves unexpectedly
- When performance issues appear in profiler
**Input required:**
- Error message or log output
- Relevant file paths and line numbers if available
- Brief description of expected vs. actual behavior
**Example prompt:**
```
"This tower stops firing after 30 seconds even though fire_rate is set to 1.0.
Error: 'get_tree()' returned null at runtime. Debug it."
```
---
### `refactor-assistant` — Refactoring Support
**Purpose:** Break up large scripts, improve code organization, add documentation.
**Capabilities:**
- Splitting monolithic scripts into smaller nodes
- Extracting methods into separate components
- Adding documentation and type hints
- Applying project conventions consistently
**When to use:**
- Scripts are growing beyond 150 lines
- Multiple unrelated behaviors in one node
- Need to improve readability before review
**Input required:**
- File path or code snippet
- Refactoring goal (e.g., "split into state machines")
- Target organization style
**Example prompt:**
```
"Refactor this 400-line EnemyMob.gd into smaller components:
1. Extract navigation logic to NavigationComponent
2. Extract attack behavior to AttackState
3. Keep health/currency handling in base class
4. Add documentation comments for each section"
```
---
### `test-generator` — Test Generation
**Purpose:** Create unit tests for game logic systems.
**Capabilities:**
- Writing Godot test cases using `@tool` functions
- Setting up mock scenes and signals
- Testing Resource-based stat changes
- Validating collision detection and hit calculations
**When to use:**
- After implementing core logic that should be validated
- Before refactoring (to preserve behavior)
- When adding critical game mechanics
**Input required:**
- System or function being tested
- Expected inputs and outputs
- Edge cases to cover
**Example prompt:**
```
"Create tests for TowerBase._can_build_at(). Test:
1. Returns true in valid tower placement zones
2. Returns false inside enemy navmesh
3. Returns false when grid coordinate exceeds bounds
4. Returns false if parent node is null"
```
---
### `design-patterns` — Design Pattern Suggestions
**Purpose:** Recommend architecture patterns suited to specific problems.
**Capabilities:**
- Suggesting ECS for complex entity management
- Recommending FSM for enemy AI states
- Proposing event-driven systems for decoupled components
- Advising on data-oriented vs. object-oriented design
**When to use:**
- Before implementing a new system
- When performance becomes an issue
- When refactoring existing code
**Input required:**
- Problem description (e.g., "I need many entities with simple movement")
- Performance constraints if any
- Preferred Godot nodes or patterns already in use
**Example prompt:**
```
"I'm building a system for 50+ projectiles that need to track targets, apply damage, and handle collision.
Current approach: individual RigidBody2D for each projectile.
What pattern would scale better?"
```
---
### `balance-helper` — Balance Analysis
**Purpose:** Analyze math relationships and suggest tuning adjustments.
**Capabilities:**
- Calculating damage per second (DPS) from fire rate and projectile stats
- Estimating kill time based on enemy HP and total damage sources
- Suggesting resource stat ranges that preserve balance space
- Identifying scaling relationships between tower levels
**When to use:**
- Before adding new towers/enemies with different power levels
- When gameplay feels too easy or difficult
- During tuning phase of wave progression
**Input required:**
- Current stats (damage, fire rate, enemy HP)
- Desired behavior description (e.g., "kill time should scale with tier")
- Resource file structure if applicable
**Example prompt:**
```
"Current: goose damage=15, fire_rate=1.0/s, chicken HP=80.
Tower Level 2 has 4 geese. Calculate total DPS and suggest enemy HP range
for level-appropriate difficulty (aim for ~30s wave duration)."
```
---
### `level-design-helper` — Level Design Assistance
**Purpose:** Plan layout, progression, and pacing for levels or zones.
**Capabilities:**
- Wave progression suggestions (spawn rates, path complexity)
- Tower placement zone recommendations
- Pacing analysis for difficulty curves
- Zone-based checkpoint design
**When to use:**
- Before creating a new level or wave sequence
- When gameplay feels too repetitive or frustrating
- During playtesting feedback analysis
**Input required:**
- Level description (size, available zones)
- Current wave structure if any
- Difficulty curve goals
**Example prompt:**
```
"I have 4 tower placement zones along a path with 3 turns.
Current waves: linear difficulty increase.
Suggest a progression that introduces new mechanics at zone boundaries."
```
---
### `doc-generator` — Documentation Generation
**Purpose:** Create documentation from existing code or planning data.
**Capabilities:**
- Generating API docs from GDScript class structures
- Writing changelogs from commit history
- Creating release notes with feature summaries
- Producing onboarding guides from project structure
**When to use:**
- Before releasing a version
- After implementing a major feature set
- When new team members join the project
**Input required:**
- Source code or commit messages
- Feature list for changelog entries
- Target audience (developers, players)
**Example prompt:**
```
"Generate API documentation for the WaveRunner system:
1. Describe each public method and its parameters
2. List signals emitted with their payloads
3. Document the Resource dependencies
4. Include usage examples"
```
---
### `onboarding-guide` — Onboarding Documentation
**Purpose:** Create setup tutorials and getting-started guides for new developers.
**Capabilities:**
- Environment setup instructions (OS-specific)
- Project structure explanation
- Editor scene loading order
- Common first tasks with examples
**When to use:**
- Before bringing on new team members
- When updating project structure significantly
- For release candidate documentation
**Input required:**
- Target OS versions supported
- Required Godot version and plugins
- Team conventions to teach
**Example prompt:**
```
"Write an onboarding guide for a new developer joining YeetGeese:
1. Explain folder structure (src/core, src/towers, etc.)
2. Show how to run the game from main.tscn
3. Describe the tower placement workflow
4. List common commands for debugging"
```
---
### `api-specs` — API Specification
**Purpose:** Define external interfaces and save/load contracts.
**Capabilities:**
- Defining save file format schemas
- Documenting modding API endpoints
- Specifying network message protocols
- Creating serialization guides
**When to use:**
- Before implementing persistent storage
- When enabling mod support
- For multiplayer networking design
**Input required:**
- Interface type (save format, mod API, network protocol)
- Required functionality list
- Platform constraints
**Example prompt:**
```
"Define a save file format for YeetGeese:
1. Must store unlocked towers and their levels
2. Must include currency progress and wave number
3. Use Godot Resource serialization (.tres files)
4. Include version field for backward compatibility"
```
---
## Command Reference
### Code Writing Commands
| Command | When to use |
|---------|-------------|
| `create file <path>` | For new classes, scenes, or resource files |
| `modify file <path>` | For adding methods or changing existing code |
| `describe scene <path>` | To generate scene tree descriptions for AI understanding |
### Review Commands
| Command | When to use |
|---------|-------------|
| `review code <path>` | For checking convention compliance |
| `optimize performance <scope>` | For profiling suggestions in specific areas |
| `debug error <message>` | For interpreting errors and suggesting fixes |
### Documentation Commands
| Command | When to use |
|---------|-------------|
| `generate docs` | For API documentation from code |
| `write changelog` | After implementing features or fixing bugs |
| `create onboarding guide` | For new developer setup instructions |
---
## Prompt Templates
### Feature Implementation Template
```
Context: I'm implementing [feature name] in [system/file].
Current state: [brief description of existing code/structure]
Goal: [what this feature should do]
Constraints: [any restrictions like "must use Resource" or "no UI changes"]
Please provide:
1. Complete, runnable GDScript 4 code with comments
2. Scene hierarchy description if new scenes are needed
3. Any new Resources (.tres) and their structure
4. Signal declarations and connection points
```
### Debugging Template
```
Error context: [where the error occurred]
Error message: [full error output]
Expected behavior: [what should have happened]
Actual behavior: [what actually happened]
Debug steps tried: [what you've already attempted]
Please provide:
1. Root cause analysis
2. Minimal fix with explanation
3. Prevention strategy for future occurrences
```
### Refactoring Template
```
Current code: [file path or snippet]
Issues to address: [e.g., "too monolithic", "missing docs"]
Refactoring goals: [what you want to achieve]
Target patterns: [any existing patterns to match]
Please provide:
1. Proposed refactored structure
2. New files and their responsibilities
3. Migration steps for existing code
```
---
## Best Practices
### 1. Always Provide Context First
Before asking for code, share:
- The relevant file path or node description
- The GDScript version being used (Godot 4.x)
- Key related signals and methods already in place
**Good:**
```
"Create an ImpactEffect.gd extending Area2D with these existing methods:
- _body_entered(var area): called on collision
- emit_signal('effect_triggered', damage, target)"
```
**Bad:**
```
"I need a collision effect for projectiles."
```
### 2. Specify Godot 4 Conventions
When requesting code, remind AI of conventions:
- Use typed signals: `signal health_changed(new_health: float)`
- Prefer `@onready` over `$` in scripts
- Type variables and return values consistently
- Keep scenes modular (compose behavior across nodes)
### 3. Reference Existing Architecture
When implementing features, reference existing patterns:
```
"Implement this using the same pattern as TowerBase.fire_at():
- Use projectile_res.instantiate() for new goose projectiles
- Spawn from $ProjectileSpawner.global_position
- Add to parent tree immediately after instantiation"
```
### 4. Specify Resource Dependencies Early
If your project uses Resources for balance:
```
"Create a Resource-based stats file for this tower:
- Fields: damage (float), fire_rate (float), cooldown_reduction (float)
- Make it loadable as a Godot Resource (.tres)
- Show how the tower reads its stats on _ready()"
```
### 5. Ask for Complete, Runnable Code
When requesting implementations:
```
"Return complete, runnable GDScript 4 code with:"
"- Comments explaining each major section"
"- Type annotations for all variables and methods"
"- Signal declarations at top of class"
```
---
## Summary
**Use these skills strategically:**
- `code-gen`: For new features and boilerplate
- `debug-helper`: When errors occur or behavior is wrong
- `refactor-assistant`: Before code grows unmanageable
- `test-generator`: After implementing critical logic
- `design-patterns`: When architecture becomes unclear
**Always provide:**
- File paths, node names, and Godot version context
- Desired behavior in terms of existing patterns
- Constraints (Resource usage, signal conventions)
**When in doubt:** Reference `AI_HELP.md` for project-specific conventions before asking questions.
---
## Note About `make-pr` Skill Availability
The **`make-pr`** skill (`.opencode/skills/make-pr.json`) is available for use! This skill will:
- Generate comprehensive, human-readable PR descriptions
- List testing strategies for each change area
- Validate changes before PR creation
- Format PR descriptions with clear sections
**When to use `make-pr` skill:**
- When ready to submit a PR from your current branch (any platform)
- Before pushing code that requires peer review
- To ensure all changes are properly documented and validated
- When creating PRs for features, fixes, or refactoring
The skill automatically adapts to your hosting platform:
- **GitHub:** Creates PR via API with comprehensive descriptions
- **Gitea:** Falls back to web UI creation (URL provided above)
**Prompt example:**
```
"Create a pull request from move_docs to develop. The changes include moving
all AI documentation files into an 'AI Docs' folder with organized structure."
```
+51 -1424
View File
File diff suppressed because it is too large Load Diff
+1445
View File
File diff suppressed because it is too large Load Diff
+1 -15
View File
@@ -22,18 +22,4 @@ When prompting an AI about this project, include:
- Flag any Godot 3 vs 4 API differences (e.g., `move_and_slide()` behavior, signal typing). - Flag any Godot 3 vs 4 API differences (e.g., `move_and_slide()` behavior, signal typing).
- If a request needs multiple scenes, describe the scene hierarchy before showing code. - If a request needs multiple scenes, describe the scene hierarchy before showing code.
Read `ARCHITECTURE.md` for system boundaries and data flow expectations.
## Handling Ambiguity & Assumptions (Crucial!)
When a request is ambiguous, incomplete, or requires knowledge outside the scope of the provided documents:
1. **DO NOT guess.** Do not write code based on assumptions you cannot validate.
2. **Acknowledge the Gap:** Explicitly state what information is missing or unclear (e.g., "The current architecture does not specify how \`WaveRunner\` handles player death.").
3. **Propose Assumptions:** If a concrete implementation path *must* be shown, list all assumptions made clearly before writing code. Use a dedicated section like:
\`\`\`markdown
**[ASSUMPTIONS MADE]**
1. We assume that the \`PlayerStats\` Resource is accessible from the global scope.
2. We are assuming the signal for enemy death will be named \`mob_killed\`.
\`\`\`
This ensures that the final code provided by AI can be reviewed against project reality before integration.
-22
View File
@@ -1,22 +0,0 @@
# 🚀 YeetGeese - Phase 0 Design Summary
**Genre:** Third-Person Over-the-Shoulder Tower Defense
**Core Fantasy:** A lone defender in a farm arena, utilizing powerful projectile geese to defend their base from waves of approaching enemies. The goal is simple but satisfying: yeet and survive!
## 🦢 Core Loop
Enemies spawn → Player maneuvers & aims → **Yeets Goose** (Damage/Stun) → Currency collected (Feathers) → Player upgrades defenses/geese → Wave ends → Repeat with increased difficulty.
## 🎯 Minimum Viable Product (MVP) Scope
| Feature | Detail | Status |
| :--- | :--- | :--- |
| **Arena** | Single, open 3D farm arena. | Defined |
| **Player** | Third-person controller (WASD + Mouse Look). Basic Health System. | Defined |
| **Weapon** | One basic Goose projectile (medium damage, spinning, self-lifetime). | Defined |
| **Enemies** | Two types: **Pigs** (Slow/Tank) & **Chickens** (Fast/Skirmisher). | Defined |
| **Progression** | 5 Waves total. Basic economy (Feathers collected from kills). | Defined |
| **UI/Win/Loss** | Health bar, Feather Count, Wave Counter. Win: Survive 5 waves. Loss: Player HP $\le$ 0. | Defined |
## ⚙️ Technical Foundation
* **Engine:** Godot 4.x (3D, Forward+).
* **Architecture:** Utilize autoloads (`GameState`, `EventBus`, etc.) to manage state centrally and decouple systems.
* **Key Systems:** Input mapping (Movement, Aim, Throw), defined collision layers (Environment, Player, Enemy, Goose, Currency).
-134
View File
@@ -1,134 +0,0 @@
# YeetGeese - Design Document
## Game Concept
**YeetGeese** is an over-the-shoulder third-person tower defense game where the player defends a base by throwing geese as powerful projectiles. The core fantasy is absurdly simple but deeply satisfying: enemies approach your objective, and you yeet geese at them to stop them.
---
## Core Fantasy
> *"I am alone in a 3D farm arena, viewed from behind my shoulders. Farm animals march toward my base. I aim with mouse, throw with a click, and watch geese crash into foes with satisfying honks, spins, and impact. Each throw feels powerful and purposeful."*
- **Player**: A lone defender in over-the-shoulder third-person
- **Weapon**: Throwable geese that act as both projectiles and temporary turrets
- **Objective**: Protect the base from waves of approaching farm animals (Pigs, Chickens)
- **Juice**: Every goose hit produces satisfying feedback (honks, feathers, screen shake)
---
## Core Loop
```
[ENEMIES SPAWN] → [PLAYER MOVES & AIMS] → [YEET GEESE] →
[GEESE DAMAGE/DISTRACT ENEMIES] → [CURRENCY COLLECTED] →
[BUY UPGRADES/NEW GEESE] → [WAVE COMPLETE?] → [NEXT WAVE (HARDER)]
```
### Breakdown:
1. **Enemies spawn** at designated points and march toward the base
2. **Player moves** through the arena in third-person, positioning for optimal throws
3. **Player aims and yeets geese** using mouse look and click
4. **Geese impact enemies**, dealing damage or causing knockback/stun
5. **Currency drops** (feathers) from defeated enemies
6. **Player spends currency** on upgrades or new goose types
7. **Wave ends** when all enemies are eliminated
8. **Next wave begins**, with more/enemy variety/difficulty
---
## MVP Scope
### One Arena
- Simple, open arena with clear spawn and base zones
- Neutral terrain allowing free movement
### One Player Controller
- **Player Controller**: Over-the-shoulder third-person view (WASD + mouse look, throw input)
- **Basic health system**
### One Goose Type
- **Basic Goose**: Standard projectile goose
- Medium damage, standard speed
- Spins on throw
- Flees after impact or short lifetime
### Two Enemy Types
1. **Pig** (formerly Grunt) Slow, low health, standard damage (meat shield)
2. **Chicken** (formerly Runner) Fast, low health, reaches base quickly
### Five Waves
| Wave | Enemies | Description |
|------|-----------------------------------|---------------------------------|
| 1 | 3 Pigs | Tutorial wave |
| 2 | 5 Pigs | Slight increase |
| 3 | 4 Pigs + 2 Chickens | Introduction of speed |
| 4 | 6 Mixed (Pig/Chicken) | Mix of both types |
| 5 | Boss wave: Oinker Boss | Large, high-health farm animal |
### Basic Economy
- **Starting currency**: 100 feathers
- **Enemy drop**: 510 feathers per kill
- **Goose cost**: Free for first type
- **Upgrade/shop unlock**: Every 10 kills unlocks new goose slot
### Basic UI
- Top-left: Health bar, feather count
- Top-right: Wave number
- Pause menu (Escape)
- Game over / Victory screens
### Win/Lose Conditions
- **Win**: Survive all 5 waves
- **Lose**: Player health reaches 0
---
## Godot Setup Summary
### Project Type
- Godot 4.x, 3D project, Forward+ renderer
### Directory Structure
```
res://
├── scripts/ # GDScript files
├── scenes/ # .tscn files
├── assets/
│ ├── audio/
│ ├── meshes/
│ ├── textures/
│ ├── materials/
│ └── animations/
├── ui/ # HUD, menus
├── data/ # Wave/upgrade configs
├── autoload/ # GameState, EventBus, AudioManager
└── tests/
```
### Autoloads
- `GameState.gd` Wave state, currency, lives, pause
- `EventBus.gd` Central signals for gameplay events
- `AudioManager.gd` SFX and music playback
- `SettingsManager.gd` Settings persistence
### Collision Layers
| Layer | Type |
|-------|-------------------------|
| 1 | Environment |
| 2 | Player |
| 3 | Enemy |
| 4 | Goose projectile |
| 5 | Goose turret |
| 6 | Currency/pickups |
| 7 | UI / trigger zones |
---
## Exit Criteria for Phase 0
- [ ] Design document is complete (this one page)
- [ ] Godot project opens cleanly in 3D mode
- [ ] Folders and autoloads are created
- [ ] Input map is configured (WASD, mouse, throw, pause)
- [ ] MVP scope is documented and understood
---
*This document defines the foundation for a small but complete playable prototype. Phase 1 will build exactly this scope; additional content comes later.*
-14
View File
@@ -1,14 +0,0 @@
# AudioManager.gd
extends Node
var music_player # Reference to AudioStreamPlayer for background music
func _ready():
print("AudioManager loaded. Ready to play sounds.")
func play_sfx(sound_path: String):
# Logic to play a sound effect
pass
func play_music(stream: AudioStream):
# Logic to set and play background music
pass
-14
View File
@@ -1,14 +0,0 @@
# SettingsManager.gd
extends Node
const SETTINGS_PATH = "user://settings.save"
func save_setting(key: String, value):
print("Saving setting: " + key)
# Logic to serialize and save settings data
pass
func load_setting(key: String):
print("Loading setting: " + key)
# Logic to read saved setting data
return null
-7
View File
@@ -1,7 +0,0 @@
# EventBus.gd
extends Node
signal event_fired(event: String, data: Dictionary)
func emit_event(event: String, data: Dictionary = {}):
print("Emitting event: " + event)
emit_signal("event_fired", data)
-18
View File
@@ -1,18 +0,0 @@
# GameState.gd
extends Node
# --- COLLISION LAYERS (TASK 4) ---
const LAYER_ENVIRONMENT = 1 # Environment/terrain
const LAYER_PLAYER = 2 # Player character
const LAYER_ENEMY = 3 # Enemy mobs
const LAYER_GOOSE_PROJECTILE = 4 # Goose projectiles
const LAYER_TURRET = 5 # Fixed towers/turrets
const LAYER_PICKUP = 6 # Currency, power-ups
const LAYER_UI_TRIGGER = 7 # UI interaction zones
var score = 0
var currency = 100
var current_wave = 0
var game_state = "PAUSED" # States: PAUSED, RUNNING, WIN, LOSE
func _ready():
print("GameState loaded. Initializing core systems.")