Project_WL/Assets/WL/Combat/Weapon/WLBlackHole.cs

116 lines
7.7 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.

// ─────────────────────────────────────────────────────────────────────────────
// WLBlackHole.cs — 맹약(오브) 각성 「블랙홀」(Skill_BlackHole): 범위 안 몬스터가 중심으로 빨려 들어가며 다단 피해 → 폭파 때 강한 피해 1회 (#978 · 2026-10-24)
//
// PD 원문: 「오브 각성 스킬이 발동되면, 해당 위치로 몬스터들이 블랙홀과 같이 빨려들어가며 다단 히트 피해를 받고, 폭파할 때 강력한 피해 1회가 발동되면 좋겠어.」
// ■ 원본(Skill_BlackHole.Co_BlackHole): 수명 동안 간격마다 범위 안 적 Attract + 피해 → 0.5 초 뒤 같은 피해 1회(+ 각성 2단계 기절)
// ■ WL — 원본 Set 끝 훅 1줄(Take)이 넘겨받는다: 매 프레임 범위 안 적을 중심 쪽으로 pullSpeed(m/s)로 끌어당김(pullMinDist 까지 · 길찾기 에이전트면 Move) ·
// 원본 간격마다 원본 투사체 피해(ProcessCollider = 피격 반응 · 이펙트 그대로) · 수명 + explodeDelay 뒤 폭파 = 피해 배율 explodeDamageMul 로 1회 + 폭발 이펙트 + 원본 기절
// ■ 값 = Resources/WL/WLBlackHole.json (C45) · enabled 0 = 원본 그대로
// 🔴 Debug.LogError 금지(원본 팝업).
// ─────────────────────────────────────────────────────────────────────────────
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace WL.Combat.Weapon
{
[System.Serializable]
public class WLBlackHoleJson
{
public int enabled = 0;
public float pullSpeed = 3f, pullMinDist = 0.4f;
public float explodeDelay = 0.5f, explodeDamageMul = 5f;
public string explodeFx = "WL/Impact/WL_Impact_Explosion";
public float explodeFxScale = 0.5f, explodeFxLife = 2f;
public string note = "";
}
public static class WLBlackHole
{
public const string ResourcesPath = "WL/WLBlackHole";
public static int Taken, Ticks, TickHits, ExplodeHits; public static float MaxPulled; public static string LastLog = "";
static WLBlackHoleJson s_cfg;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] static void Reset() { s_cfg = null; }
internal static WLBlackHoleJson Cfg
{
get
{
if (s_cfg != null) return s_cfg;
s_cfg = new WLBlackHoleJson();
var ta = Resources.Load<TextAsset>(ResourcesPath);
if (ta == null) { LastLog = "설정 없음 " + ResourcesPath; return s_cfg; }
try { JsonUtility.FromJsonOverwrite(ta.text, s_cfg); } catch (System.Exception ex) { LastLog = "설정 파싱 실패 — " + ex.Message; }
return s_cfg;
}
}
/// <summary>원본 Skill_BlackHole.Set 훅 — true 면 WL 이 진행(원본 Co_BlackHole 건너뜀). 값은 원본이 각성 단계까지 계산한 것.</summary>
public static bool Take(ProjectileBase pb, float range, float life, float gap, float stun)
{
try
{
var c = Cfg; if (c.enabled == 0 || pb == null) return false;
var pd = pb.Get_ProjectTileData(); if (pd == null || DSUtil.CheckNull(pd.shooter)) return false;
var run = pb.GetComponent<WLBlackHoleRunner>(); if (run == null) run = pb.gameObject.AddComponent<WLBlackHoleRunner>();
run.Begin(pb, pd, range, life, Mathf.Max(0.05f, gap), stun, c); Taken++;
return true;
}
catch (System.Exception ex) { LastLog = "넘겨받기 실패 — " + ex.Message; return false; }
}
}
internal sealed class WLBlackHoleRunner : MonoBehaviour
{
public void Begin(ProjectileBase pb, ProjectileData pd, float range, float life, float gap, float stun, WLBlackHoleJson c)
{
StopAllCoroutines(); StartCoroutine(Run(pb, pd, range, life, gap, stun, c));
}
static List<Actor> Enemies(ProjectileData pd, Vector3 center, float range)
{
var res = new List<Actor>(); var info = ActorInfo.Ins; if (info == null || DSUtil.CheckNull(pd.shooter)) return res;
var list = info.Get_EnemyActors(pd.shooter.IsEnemy(), center, range + 3f); if (list == null) return res;
try { for (int k = 0; k < list.Count; k++) { var a = list[k]; if (a == null || a.IsDead()) continue; var v = a.Get_position() - center; v.y = 0f; if (v.magnitude <= range + WL.Combat.WLCombatMotionSettings.BodyRadius(a)) res.Add(a); } }
finally { info.Release_Actors(list); }
return res;
}
static int HitAll(ProjectileBase pb, List<Actor> list)
{
int n = 0; foreach (var a in list) { if (a == null || a.IsDead()) continue; var col = a.GetComponent<Collider>(); if (col != null && col.enabled && pb.ProcessCollider(col, false)) n++; }
return n;
}
IEnumerator Run(ProjectileBase pb, ProjectileData pd, float range, float life, float gap, float stun, WLBlackHoleJson c)
{
Vector3 center = pb.transform.position; float t0 = Time.time, nextTick = 0f, pulled = 0f;
while (Time.time - t0 < life)
{
float t = Time.time - t0, step = c.pullSpeed * Time.deltaTime;
foreach (var a in Enemies(pd, center, range)) // 빨려 들어감 — 중심 쪽 수평 이동(pullMinDist 까지)
{
var v = center - a.Get_position(); v.y = 0f; float d = v.magnitude - c.pullMinDist; if (d <= 0f) continue;
var delta = v.normalized * Mathf.Min(step, d);
var ag = a.GetComponent<UnityEngine.AI.NavMeshAgent>();
if (ag != null && ag.enabled && ag.isOnNavMesh) ag.Move(delta); else a.transform.position += delta;
pulled += delta.magnitude;
}
if (t >= nextTick) { nextTick += gap; WLBlackHole.Ticks++; WLBlackHole.TickHits += HitAll(pb, Enemies(pd, center, range)); }
yield return null;
}
if (c.explodeDelay > 0f) yield return new WaitForSeconds(c.explodeDelay);
// 폭파 — 피해 배율을 잠시 올려 1회 · 기절 · 폭발 이펙트
var targets = Enemies(pd, center, range); float mul0 = pd.AddDmgPercent;
pd.AddDmgPercent = mul0 * Mathf.Max(1f, c.explodeDamageMul);
try { WLBlackHole.ExplodeHits += HitAll(pb, targets); } finally { pd.AddDmgPercent = mul0; }
if (stun > 0f) foreach (var a in targets) if (a != null && !a.IsDead()) a.Stun(stun, true);
var fx = !string.IsNullOrEmpty(c.explodeFx) ? Resources.Load<GameObject>(c.explodeFx) : null;
if (fx != null) { var g = Instantiate(fx, center, Quaternion.identity); g.transform.localScale = fx.transform.localScale * c.explodeFxScale; foreach (var ps in g.GetComponentsInChildren<ParticleSystem>(true)) { var m = ps.main; m.scalingMode = ParticleSystemScalingMode.Hierarchy; } Destroy(g, Mathf.Max(0.5f, c.explodeFxLife)); }
WLBlackHole.MaxPulled = Mathf.Max(WLBlackHole.MaxPulled, pulled);
WLBlackHole.LastLog = "범위 " + range.ToString("F1") + " m · 수명 " + life.ToString("F1") + " s · 간격 " + gap.ToString("F2") + " · 다단 누적 " + WLBlackHole.TickHits + " · 폭파 명중 " + WLBlackHole.ExplodeHits + " × 배율 " + c.explodeDamageMul + (fx == null ? " · 폭발 이펙트 없음 " + c.explodeFx : "");
WLStaffEarth.OffOriginal(pb);
}
}
}