Enemy foundation: definitions, capability components, and damage to the fleet #44
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Part of #40. Prerequisite for both new enemy archetypes.
Two problems block them today:
Enemy.gdis one hardcoded chaser with no definition/component structure, unlikeShip/Building/vessels after #34-#35.Ship._on_area_enteredonly reacts to physical contact withenemies/asteroids, andProjectile.gdis player-only (player_projectiles->enemies). Both requested enemies are ranged, so the reverse damage path has to exist first.Enemy.gdresolves the fleet with@onready var fleet := get_node("../Fleet"), which breaks the moment an enemy is parented underSurfaceFeatureson a planet surface.Scope - A. Definitions and components
EnemyDefinition.gd-class_name EnemyDefinition extends Resource:enemy_id,display_name,max_hp,speed,acceleration,rotation_speed,hull_points: PackedVector2Array,glow_color,component_scenes: Array[PackedScene]. MirrorVesselDefinition.gd.EnemyDefinitions.gd- registry withget_definition(id)and a default, mirroringVesselDefinitions.gd.Enemy.gd-@export var enemy_id: String = "chaser"; resolve the definition in_ready(), instantiatecomponent_scenesas children, addget_component(capability)/has_capability(capability), tick components from_physics_process(thehas_method("tick")loopShip.gduses), and drawdefinition.hull_pointsindefinition.glow_color.EnemyPursuitComponent.tscn+.gd(capability_id = "pursuit") - today's chase-the-nearest-ship movement, moved out ofEnemy.gdverbatim.ChaserEnemyDefinition.tres- reproduces the current enemy exactly: speed 140, acceleration 260, rotation_speed 6, max_hp 3, hull[(0,-16),(12,0),(0,16),(-12,0)], colourColor(1.0, 0.15, 0.2), pursuit component.Fleetto aplayer_fleetgroup inFleet._ready()and resolve viaget_tree().get_first_node_in_group("player_fleet"). Remove everyget_node("../Fleet")from enemy code.EnemySpawner.gdkeeps working unchanged in space, spawning the chaser.Scope - B. Ranged damage to the fleet
EnemyProjectile.tscn+EnemyProjectile.gd- mirrorsProjectile.gdbut joins groupenemy_projectilesand damagesplayer_ships. Exportedspeed,damage,lifetime, colour.Ship.gd- extract the damage path out of_on_area_enteredintofunc take_damage(amount: int) -> void: offer the hit to components implementingabsorb_hit()(the shield) first, then explode and free._on_area_enteredbecomes contact ->take_damage(1). Preserve exactly the existing behaviour where a shielded contact hit also destroys the colliding asteroid.EnemyProjectile._on_area_enteredcallstake_damage(damage)onplayer_shipsmembers and frees itself.Out of scope
The sniper and artillery enemies themselves - they are separate tickets that consume this.
Acceptance criteria
tests/test_enemy_combat.gd,tests/test_enemy_collision.gdandtests/test_shield_vessel.gdpass with behaviour unchanged.Fleetstill finds the fleet and pursues it.enemy_idstring comparison exists outsideEnemyDefinitions.gd.Tests
tests/test_enemy_composition.gdandtests/test_enemy_projectile_damage.gd.bash tests/run_tests.sh enemy, then the full suite.Repo notes (read before starting)
/storage/emulated/0/GodotProjects/SpaceshipTest.touchon it. Android FUSE does not bump mtime under~/storage/shared, and Godot's import cache will silently keep serving the old version, making a correct fix look like it did nothing.bash tests/run_tests.sh <substring>; full suite with no argument. Tests areSceneTreescripts printingTEST_NAME: PASS/TEST_NAME: FAIL (reason)and callingquit(0|1)- copy the shape oftests/test_gas_bomber_cooldowns.gd.bash tests/screenshot.sh <res://Scene.tscn> <out.png> <frames> 720x1280. Use few frames (~10-20); the software rasterizer is slow.--headlessrenders nothing at all, so it cannot validate visuals.NeonDraw/NeonPiece; do not introduce a different visual idiom.*Definitionresources plus capability component scenes. Do not addif type == "..."branches.FileAccess, nouser://anywhere outside this ticket's explicit scope).GameStateis an in-memory autoload. Do not invent a save system.Implemented and pushed as
f14ee65.EnemyDefinition/EnemyDefinitions+ capability components (EnemyPursuitComponentholds today's chase logic, moved verbatim out ofEnemy.gd), same shape asShip.gd/VesselDefinition.player_fleetgroup instead of a hardcoded../Fleetsibling path, so one parented anywhere (e.g. under a planet surface'sSurfaceFeatures) still finds it - this was a real bug that would have hit both #45 and #46 the moment an enemy spawned on a landed surface.Ship.take_damage()is now a proper entry point (contact damage routes through it unchanged, verified the shielded-hit-destroys-the-asteroid behavior is preserved exactly), andEnemyProjectilelets a ranged component fire atplayer_ships.New tests
tests/test_enemy_composition.gd(definition-driven stats, works from a non-sibling parent) andtests/test_enemy_projectile_damage.gd(unshielded ship dies, shielded ship absorbs and loses exactly one charge). Full 71/71 regression suite passed clean, including one real fix caught along the way: two existing tests setenemy.speed = 0.0beforeadd_child()to freeze a test enemy in place, which the new_ready()-driven stat copy was clobbering - moved those overrides to afteradd_child().#45 and #46 build directly on this.