Integrate Kenney Starter Kit FPS assets + comprehensive test suite
Assets (CC0 license): - 3D models: Blaster gun, walls, platforms, grass, clouds, enemy - Sounds: blaster fire, impacts, footsteps, jumps, weapon change - Sprites: crosshair, hit marker, muzzle flash, skybox, blob shadow - Font: Lilita One Code changes: - weapon_manager.gd: load blaster.glb as gun model (replaces box mesh) - player.tscn: use Kenney sounds (blaster.ogg, enemy_hurt/destroy) and crosshair Test suite (100 unit tests + 4 integration scenarios): - weapon_data.gd: 27 tests — all 6 weapons every stat verified - economy.gd: 19 tests — constants, loss streak, buy thresholds - bomb.gd: 15 tests — state machine, timing constants - round_manager.gd: 10 tests — win conditions, elimination logic - team_manager.gd: 8 tests — auto-balance, team assignment - headless_test_bot.gd: integration bot with movement/idle/rounds scenarios - run_multi_bot.sh: multi-client launcher
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
extends Node3D
|
||||
|
||||
@export var player: Node3D
|
||||
|
||||
@onready var raycast = $RayCast
|
||||
@onready var muzzle_a = $MuzzleA
|
||||
@onready var muzzle_b = $MuzzleB
|
||||
|
||||
var health := 100
|
||||
var time := 0.0
|
||||
var target_position: Vector3
|
||||
var destroyed := false
|
||||
|
||||
# When ready, save the initial position
|
||||
|
||||
func _ready():
|
||||
target_position = position
|
||||
|
||||
|
||||
func _process(delta):
|
||||
self.look_at(player.position + Vector3(0, 0.5, 0), Vector3.UP, true) # Look at player
|
||||
target_position.y += (cos(time * 5) * 1) * delta # Sine movement (up and down)
|
||||
|
||||
time += delta
|
||||
|
||||
position = target_position
|
||||
|
||||
# Take damage from player
|
||||
|
||||
func damage(amount):
|
||||
Audio.play("sounds/enemy_hurt.ogg")
|
||||
|
||||
health -= amount
|
||||
|
||||
if health <= 0 and !destroyed:
|
||||
destroy()
|
||||
|
||||
# Destroy the enemy when out of health
|
||||
|
||||
func destroy():
|
||||
Audio.play("sounds/enemy_destroy.ogg")
|
||||
|
||||
destroyed = true
|
||||
queue_free()
|
||||
|
||||
# Shoot when timer hits 0
|
||||
|
||||
func _on_timer_timeout():
|
||||
raycast.force_raycast_update()
|
||||
|
||||
if raycast.is_colliding():
|
||||
var collider = raycast.get_collider()
|
||||
|
||||
if collider.has_method("damage"): # Raycast collides with player
|
||||
|
||||
# Play muzzle flash animation(s)
|
||||
|
||||
muzzle_a.frame = 0
|
||||
muzzle_a.play("default")
|
||||
muzzle_a.rotation_degrees.z = randf_range(-45, 45)
|
||||
|
||||
muzzle_b.frame = 0
|
||||
muzzle_b.play("default")
|
||||
muzzle_b.rotation_degrees.z = randf_range(-45, 45)
|
||||
|
||||
Audio.play("sounds/enemy_attack.ogg")
|
||||
|
||||
collider.damage(5) # Apply damage to player
|
||||
Reference in New Issue
Block a user