# YeetGeese Architecture Notes ## Core Loop 1. `WaveRunner` reads a `WaveTable` Resource → spawns enemies onto a shared `NavigationRegion2D` path. 2. Player drags a tower placement cursor → confirms → instantiates a tower scene at the grid cell. 3. Towers run a continuous `find_target()` → call `fire()` → spawn a goose projectile from `ProjectileSpawner`. 4. Geese travel to the target, deal damage, and grant currency on kill. ## Key Design Choices - **Stats as Resources:** Every balanceable value (damage, fire rate, enemy HP) lives in `.tres`/`.tres` files, not hardcoded. This keeps tuning non-invasive. - **Signals over polling:** Towers emit `target_acquired`, enemies emit `health_changed`, HUD listens via `%HUD`. No frame-polling managers. - **Placement grid decoupled from navigation:** The buildable area is a `Vector2i` grid checked against `_can_build_at()`. Pathfinding uses Godot's built-in `NavigationServer2D`. ## Scene Expectations (for AI) - Tower: `TowerBase.gd` (Node2D) with child `ProjectileSpawner` (Marker2D) and `TargetFinder`. - Enemy: `EnemyMob.gd` extending `Area2D`, uses `_ready()` to assign a path via `$NavigationPath2D.bake_path_from_parent()`. - Projectile: `Goose.gd` extending `RigidBody2D` or `CharacterBody2D` with linear velocity toward target. ## Data Flow Example ```gdscript # WaveRunner creates an enemy from a resource var mob_scene = enemy_res.instantiate().tree_entered.connect(_on_mob_spawned) add_child(mob_scene) # Tower acquires and fires func fire_at(target: Node2D) -> void: var goose := projectile_res.instantiate() as Goose add_child(goose) goose.yeet_from($ProjectileSpawner.global_position, target.global_position)