EerieVillage/Assets/Scripts/Skills/Effectors/PiercingProjectile.cs

89 lines
3.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections.Generic;
using UnityEngine;
using Platformer.Mechanics;
using Platformer.Gameplay;
using static Platformer.Core.Simulation;
namespace EerieVillage.Skills.Effectors
{
/// <summary>
/// A13 천둥발 변경 컨셉 (PD 지시 2026-05-13).
/// FX_Lightningball 투사체 — 천천히 전진·관통·경로 닿는 적 매 0.2초마다 영역 데미지.
///
/// 기존 Projectile 와 다름:
/// - 적 명중 후 SelfDestruct X (관통)
/// - 각 적별 마지막 tick 시각 추적·DotInterval (0.2s) 영역 영역 데미지
/// </summary>
public class PiercingProjectile : Projectile
{
// 적별 마지막 hit 시각
readonly Dictionary<EnemyController, float> _lastHitTime = new Dictionary<EnemyController, float>();
public override void Initialize(ActiveSkillRuntime runtime, PlayerSkillInventory inventory, Vector2 direction)
{
base.Initialize(runtime, inventory, direction);
_speed = 2.5f; // PD 지시 2026-05-13 — A13 천둥발 천천히 전진 (기본 6 → 2.5)
_lifetime = 6f; // 관통이므로 lifetime 길게
_lastHitTime.Clear();
// PD 정합 2026-05-13 — OnTriggerStay2D 발화 영역 Kinematic Rigidbody2D + useFullKinematicContacts 영역
// Enemy 영역 Kinematic Rb → Kinematic vs Kinematic OnTriggerStay2D 영역 기본 발화 X.
// useFullKinematicContacts=true 영역 영역 발화 정합.
var rb = GetComponent<Rigidbody2D>();
if (rb == null) rb = gameObject.AddComponent<Rigidbody2D>();
rb.bodyType = RigidbodyType2D.Kinematic;
rb.simulated = true;
rb.gravityScale = 0f;
rb.useFullKinematicContacts = true;
}
// PD 정합 2026-05-13 — OnTrigger 영역 폐기 (Kinematic vs Kinematic 발화 영역 영역 X).
// Update 영역 OverlapBox 영역 매 frame 직접 검사·적별 0.2s 간격 영역 (다른 액티브 정합).
protected override void OnTriggerEnter2D(Collider2D other) { /* no-op */ }
protected override void Update()
{
base.Update(); // 이동·페이드·SyncHitboxToData
if (_data == null) return;
float interval = (_data.DotInterval > 0.01f) ? _data.DotInterval : 0.2f;
float now = Time.time;
var cf = new ContactFilter2D();
cf.useTriggers = false;
int enemyLayer = LayerMask.NameToLayer("Enemy");
if (enemyLayer >= 0) { cf.SetLayerMask(1 << enemyLayer); cf.useLayerMask = true; }
var results = new Collider2D[16];
// 박스 영역 transform.position + collider.size × localScale 영역 정합
var box = GetComponent<BoxCollider2D>();
Vector2 worldSize = box != null
? new Vector2(box.size.x * Mathf.Abs(transform.lossyScale.x), box.size.y * Mathf.Abs(transform.lossyScale.y))
: (Vector2)_data.HitboxSize;
int n = Physics2D.OverlapBox(transform.position, worldSize, transform.eulerAngles.z, cf, results);
int damage = Mathf.Max(_data.BaseDamage, 1);
for (int i = 0; i < n; i++)
{
var c = results[i];
if (c == null) continue;
var enemy = c.GetComponent<EnemyController>();
if (enemy == null) continue;
var health = c.GetComponent<Health>();
if (health == null || !health.IsAlive) continue;
float last;
if (_lastHitTime.TryGetValue(enemy, out last))
{
if (now - last < interval) continue;
}
_lastHitTime[enemy] = now;
health.DecrementBypassInvulnWithHit(damage);
if (!health.IsAlive)
{
Schedule<EnemyDeath>().enemy = enemy;
}
}
}
}
}