✨ Turret shooting and damaing
This commit is contained in:
97
Scripts/Entities/Bullet.cs
Normal file
97
Scripts/Entities/Bullet.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using Godot;
|
||||
|
||||
namespace AceFieldNewHorizon.Scripts.Entities;
|
||||
|
||||
public partial class Bullet : Area2D
|
||||
{
|
||||
[Export] public float Speed = 400.0f;
|
||||
[Export] public int Damage = 10;
|
||||
[Export] public float MaxDistance = 1000.0f;
|
||||
|
||||
private Vector2 _direction = Vector2.Right;
|
||||
private Vector2 _startPosition;
|
||||
private float _distanceTraveled = 0f;
|
||||
private bool _hasHit = false;
|
||||
private uint _ignoreCollisionLayer = 0; // Layer to ignore (will be set by turret)
|
||||
|
||||
public void Initialize(Vector2 direction, Vector2 position, float rotation, uint ignoreLayer = 0)
|
||||
{
|
||||
_direction = direction.Normalized();
|
||||
Position = position;
|
||||
Rotation = rotation;
|
||||
_startPosition = position;
|
||||
_ignoreCollisionLayer = ignoreLayer;
|
||||
|
||||
// Connect the area entered signal
|
||||
BodyEntered += OnBodyEntered;
|
||||
AreaEntered += OnAreaEntered;
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
if (_hasHit) return;
|
||||
|
||||
// Move the bullet
|
||||
var movement = _direction * Speed * (float)delta;
|
||||
Position += movement;
|
||||
_distanceTraveled += movement.Length();
|
||||
|
||||
// Check if bullet has traveled max distance
|
||||
if (_distanceTraveled >= MaxDistance)
|
||||
{
|
||||
QueueFree();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBodyEntered(Node2D body)
|
||||
{
|
||||
HandleCollision(body);
|
||||
}
|
||||
|
||||
private void OnAreaEntered(Area2D area)
|
||||
{
|
||||
HandleCollision(area);
|
||||
}
|
||||
|
||||
private void HandleCollision(Node2D node)
|
||||
{
|
||||
if (_hasHit) return;
|
||||
|
||||
// Skip collision if it's on the ignore layer
|
||||
if (node is PhysicsBody2D physicsBody &&
|
||||
(physicsBody.CollisionLayer & _ignoreCollisionLayer) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_hasHit = true;
|
||||
|
||||
// If we hit an enemy, deal damage
|
||||
if (node is Enemy enemy)
|
||||
{
|
||||
// Get the global position where the bullet hit
|
||||
var hitPosition = GlobalPosition;
|
||||
enemy.TakeDamage(Damage, hitPosition);
|
||||
}
|
||||
|
||||
// Optional: Add hit effect here
|
||||
// CreateHitEffect();
|
||||
|
||||
// Remove the bullet
|
||||
QueueFree();
|
||||
}
|
||||
|
||||
private void CreateHitEffect()
|
||||
{
|
||||
// You can add a hit effect here if desired
|
||||
// For example, a small explosion or impact sprite
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
// Clean up signal connections
|
||||
BodyEntered -= OnBodyEntered;
|
||||
AreaEntered -= OnAreaEntered;
|
||||
}
|
||||
}
|
1
Scripts/Entities/Bullet.cs.uid
Normal file
1
Scripts/Entities/Bullet.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://vgx2a8gm7l8b
|
@@ -11,14 +11,52 @@ public partial class Enemy : CharacterBody2D
|
||||
[Export] public float AttackRange = 50.0f;
|
||||
[Export] public int Damage = 10;
|
||||
[Export] public float AttackCooldown = 1.0f;
|
||||
[Export] public int MaxHealth = 100;
|
||||
[Export] public bool ShowDamageNumbers = true;
|
||||
|
||||
private Player _player;
|
||||
private float _attackTimer = 0;
|
||||
private bool _isPlayerInRange = false;
|
||||
private Area2D _detectionArea;
|
||||
private int _currentHealth;
|
||||
private ProgressBar _healthBar;
|
||||
|
||||
public int CurrentHealth
|
||||
{
|
||||
get => _currentHealth;
|
||||
private set
|
||||
{
|
||||
_currentHealth = Mathf.Clamp(value, 0, MaxHealth);
|
||||
UpdateHealthBar();
|
||||
|
||||
if (_currentHealth <= 0)
|
||||
{
|
||||
Die();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsDead => _currentHealth <= 0;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_currentHealth = MaxHealth;
|
||||
|
||||
// Create health bar
|
||||
_healthBar = new ProgressBar
|
||||
{
|
||||
MaxValue = MaxHealth,
|
||||
Value = _currentHealth,
|
||||
Size = new Vector2(40, 4),
|
||||
ShowPercentage = false,
|
||||
Visible = false
|
||||
};
|
||||
|
||||
var healthBarContainer = new Control();
|
||||
healthBarContainer.AddChild(_healthBar);
|
||||
AddChild(healthBarContainer);
|
||||
healthBarContainer.Position = new Vector2(-20, -20); // Adjust position as needed
|
||||
|
||||
// Create detection area
|
||||
_detectionArea = new Area2D();
|
||||
var collisionShape = new CollisionShape2D();
|
||||
@@ -37,6 +75,8 @@ public partial class Enemy : CharacterBody2D
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
if (IsDead) return;
|
||||
|
||||
if (_player != null && _isPlayerInRange)
|
||||
{
|
||||
// Face the player
|
||||
@@ -60,8 +100,68 @@ public partial class Enemy : CharacterBody2D
|
||||
}
|
||||
}
|
||||
|
||||
public void TakeDamage(int damage, Vector2? hitPosition = null)
|
||||
{
|
||||
if (IsDead) return;
|
||||
|
||||
CurrentHealth -= damage;
|
||||
|
||||
// Show damage number (optional)
|
||||
if (ShowDamageNumbers)
|
||||
{
|
||||
var damageLabel = new Label
|
||||
{
|
||||
Text = $"-{damage}",
|
||||
Position = hitPosition ?? GlobalPosition,
|
||||
ZIndex = 1000
|
||||
};
|
||||
|
||||
GetTree().CurrentScene.AddChild(damageLabel);
|
||||
|
||||
// Animate and remove damage number
|
||||
var tween = CreateTween();
|
||||
tween.TweenProperty(damageLabel, "position:y", damageLabel.Position.Y - 30, 0.5f);
|
||||
tween.TweenCallback(Callable.From(() => damageLabel.QueueFree())).SetDelay(0.5f);
|
||||
}
|
||||
|
||||
// Visual feedback
|
||||
var originalModulate = Modulate;
|
||||
Modulate = new Color(1, 0.5f, 0.5f); // Flash red
|
||||
|
||||
var tweenFlash = CreateTween();
|
||||
tweenFlash.TweenProperty(this, "modulate", originalModulate, 0.2f);
|
||||
}
|
||||
|
||||
private void UpdateHealthBar()
|
||||
{
|
||||
if (_healthBar != null)
|
||||
{
|
||||
_healthBar.Value = _currentHealth;
|
||||
_healthBar.Visible = _currentHealth < MaxHealth; // Only show when damaged
|
||||
}
|
||||
}
|
||||
|
||||
private void Die()
|
||||
{
|
||||
// Play death animation/sound
|
||||
// You can add a death animation here
|
||||
|
||||
// Disable collisions and hide
|
||||
SetProcess(false);
|
||||
SetPhysicsProcess(false);
|
||||
Hide();
|
||||
|
||||
// Queue free after a delay (for any death animation/sound to play)
|
||||
var timer = new Timer();
|
||||
AddChild(timer);
|
||||
timer.Timeout += () => QueueFree();
|
||||
timer.Start(0.5f);
|
||||
}
|
||||
|
||||
private void TryAttackPlayer(double delta)
|
||||
{
|
||||
if (IsDead) return;
|
||||
|
||||
_attackTimer += (float)delta;
|
||||
|
||||
if (_attackTimer >= AttackCooldown)
|
||||
|
Reference in New Issue
Block a user