using System.Collections.Generic; using UnityEngine; using Platformer.Mechanics; using Platformer.Gameplay; using static Platformer.Core.Simulation; namespace EerieVillage.Skills.Effectors { /// /// 투사체 기본 컴포넌트. Line 궤적 직선 이동·단일 적 타격 후 소멸. /// BT12-Dev Phase 2-B §4-2. /// 파생: (A15 추적 화염구). /// public class Projectile : MonoBehaviour { protected ActiveSkillData _data; protected ActiveSkillRuntime _runtime; protected PlayerSkillInventory _inventory; protected Vector2 _direction; protected float _speed = 12f; protected float _lifetime = 3f; // BT12-Dev 2026-05-10 (PD #1·#2) — 거리 제한·벽 충돌 영역 protected Vector2 _spawnPosition; protected float _maxRange; // 동일 투사체로 동일 Collider 중복 타격 방지 protected readonly HashSet _hitTargets = new HashSet(); /// /// ProjectileSpawner.Trigger 에서 Instantiate 직후 호출. /// public virtual void Initialize(ActiveSkillRuntime runtime, PlayerSkillInventory inventory, Vector2 direction) { _runtime = runtime; _data = runtime.ActiveData; _inventory = inventory; _direction = direction.normalized; _hitTargets.Clear(); // BT12-Dev 2026-05-10 (PD #1) — 거리 제한 영역 영역 spawn 위치 저장 _spawnPosition = transform.position; // 최대 거리 = Camera 영역 영역 영역 × 1.5 (PD #1 명시) // Camera ortho size 3.5·aspect 16:9 → height 7·width ~12.44 → maxRange ~18.66 unit var cam = Camera.main; if (cam != null && cam.orthographic) { float camWidth = cam.orthographicSize * 2f * cam.aspect; _maxRange = camWidth * 1.5f; } else { _maxRange = 20f; // fallback } // Phase 2-B: 풀링 미도입 — Invoke 기반 자동 소멸 (거리 제한 영역 영역 영역 영역 영역 안전망) Invoke(nameof(SelfDestruct), _lifetime); } protected virtual void Update() { transform.position += (Vector3)(_direction * _speed * Time.deltaTime); // BT12-Dev 2026-05-10 (PD #1) — 거리 제한 영역 영역 SelfDestruct if (Vector2.Distance(transform.position, _spawnPosition) >= _maxRange) { SelfDestruct(); } } protected virtual void OnTriggerEnter2D(Collider2D other) { if (_hitTargets.Contains(other)) return; // PD 지시 2026-05-09 후속 방어 — 자기(Player) hit·자기 자신·hit 방어. if (other.GetComponent() != null) return; // Enemy 레이어 한정. int enemyLayer = LayerMask.NameToLayer("Enemy"); bool isEnemy = (enemyLayer != -1 && other.gameObject.layer == enemyLayer) || other.GetComponent() != null; if (isEnemy) { var health = other.GetComponent(); if (health == null || !health.IsAlive) return; _hitTargets.Add(other); // 유효 대미지 산출 — BT12-Dev 2026-05-10 임시 (PD 지시): 기본 공격력 5 하한 강제. int damage = Mathf.Max(_runtime.CalculateEffectiveDamage(), 5); // 피해 적용 health.Decrement(damage); // 부가 효과 (DoT·Stun·Slow·DebuffStack) — StatusApplier 위임 var enemy = other.GetComponent(); if (enemy != null) { StatusApplier.Apply(_data, enemy); } // Enemy 즉사 시 EnemyDeath 체인 발동 if (!health.IsAlive && enemy != null) { Schedule().enemy = enemy; } // 단일 적 타격 후 소멸 (관통 미지원 — Phase 2 범위 내) SelfDestruct(); return; } // BT12-Dev 2026-05-10 (PD #2) — 벽 충돌 시 SelfDestruct. // Layer 0 (Default·Ground) · Layer 16 (Foreground·발판) 영역 영역 Tilemap·Composite·Box collider 영역 정합. // 레이저 영역 영역 영역 영역 영역 X — 본 Projectile 영역 영역 (영역 영역 영역 영역 X) — 모든 Projectile 영역 SelfDestruct. int otherLayer = other.gameObject.layer; bool isWall = (otherLayer == 0 || otherLayer == 16); if (isWall) { SelfDestruct(); } } protected void SelfDestruct() { CancelInvoke(nameof(SelfDestruct)); Destroy(gameObject); } } }