Files
AceField-New-Horizon/Scripts/Tiles/EnemyPortalTile.cs
2025-08-31 19:30:16 +08:00

130 lines
3.9 KiB
C#

using AceFieldNewHorizon.Scripts.Entities;
using Godot;
namespace AceFieldNewHorizon.Scripts.Tiles;
public partial class EnemyPortalTile : BaseTile
{
[Export] public PackedScene EnemyScene;
[Export] public int MaxEnemies = 5;
[Export] public float SpawnDelay = 5.0f; // Time between spawn attempts
[Export] public float SpawnRadius = 50.0f; // Radius around the nest where enemies can spawn
[Export] public bool Active = true;
private Timer _spawnTimer;
private int _currentEnemyCount;
public override void _Ready()
{
base._Ready();
// Add to Hostile group to prevent enemies from attacking their own nest
AddToGroup("Hostile");
// Create and configure the timer
_spawnTimer = new Timer
{
Autostart = true,
WaitTime = SpawnDelay
};
AddChild(_spawnTimer);
_spawnTimer.Timeout += OnSpawnTimerTimeout;
var sprite = GetNode<Sprite2D>("Sprite2D");
// Create and configure the shadow sprite
var shadow = new Sprite2D
{
Texture = sprite.Texture,
Scale = sprite.Scale * 1.05f, // Slightly larger than the original
Modulate = new Color(0, 0, 0, 0.5f), // Slightly more transparent
Position = new Vector2(0, 6), // Closer to the sprite (reduced from 30)
ZIndex = -1
};
AddChild(shadow);
// Create floating animation
const float floatOffset = 5.0f;
const float floatDuration = 2.0f;
var tween = CreateTween().SetLoops();
tween.TweenProperty(sprite, "position:y", -floatOffset, floatDuration)
.SetEase(Tween.EaseType.InOut)
.SetTrans(Tween.TransitionType.Sine);
tween.TweenProperty(sprite, "position:y", floatOffset, floatDuration * 2)
.SetEase(Tween.EaseType.InOut)
.SetTrans(Tween.TransitionType.Sine);
tween.TweenProperty(sprite, "position:y", 0, floatDuration)
.SetEase(Tween.EaseType.InOut)
.SetTrans(Tween.TransitionType.Sine);
// Animate shadow
tween.Parallel().TweenProperty(shadow, "position:y", 12 - (floatOffset * 0.3f), floatDuration)
.SetEase(Tween.EaseType.InOut)
.SetTrans(Tween.TransitionType.Sine);
tween.Parallel().TweenProperty(shadow, "scale", sprite.Scale * 1.02f, floatDuration)
.SetEase(Tween.EaseType.InOut)
.SetTrans(Tween.TransitionType.Sine);
tween.Parallel().TweenProperty(shadow, "modulate:a", 0.6f, floatDuration)
.SetEase(Tween.EaseType.InOut)
.SetTrans(Tween.TransitionType.Sine);
tween.Parallel().TweenProperty(shadow, "position:y", 12 + (floatOffset * 0.4f), floatDuration * 2)
.SetEase(Tween.EaseType.InOut)
.SetTrans(Tween.TransitionType.Sine)
.SetDelay(floatDuration);
tween.Parallel().TweenProperty(shadow, "scale", sprite.Scale * 1.08f, floatDuration * 2)
.SetEase(Tween.EaseType.InOut)
.SetTrans(Tween.TransitionType.Sine)
.SetDelay(floatDuration);
tween.Parallel().TweenProperty(shadow, "modulate:a", 0.4f, floatDuration * 2)
.SetEase(Tween.EaseType.InOut)
.SetTrans(Tween.TransitionType.Sine)
.SetDelay(floatDuration);
}
private void OnSpawnTimerTimeout()
{
if (!Active || EnemyScene == null || _currentEnemyCount >= MaxEnemies)
return;
// Check if we can spawn more enemies
var enemies = GetTree().GetNodesInGroup("Enemy");
_currentEnemyCount = enemies.Count;
if (_currentEnemyCount >= MaxEnemies)
return;
// Spawn a new enemy
var enemy = EnemyScene.Instantiate<Enemy>();
if (enemy != null)
{
GetParent().AddChild(enemy);
// Calculate a random position within the spawn radius
var randomAngle = GD.Randf() * Mathf.Pi * 2;
var randomOffset = new Vector2(
Mathf.Cos(randomAngle) * SpawnRadius,
Mathf.Sin(randomAngle) * SpawnRadius
);
enemy.GlobalPosition = GlobalPosition + randomOffset;
_currentEnemyCount++;
// Connect to the enemy's death signal if available
enemy.TreeExiting += () => OnEnemyDied();
}
}
private void OnEnemyDied()
{
_currentEnemyCount = Mathf.Max(0, _currentEnemyCount - 1);
}
public void SetActive(bool active)
{
Active = active;
_spawnTimer.Paused = !active;
}
}