476 lines
26 KiB
C#
476 lines
26 KiB
C#
// ─────────────────────────────────────────────────────────────────────────────
|
||
// WLCombatTextSettings.cs — 전투 텍스트 연출 값의 단일 출처 (WL-813g · #813)
|
||
//
|
||
// 기준서 v1 §B 요소 2 「데미지 숫자 = 일반 0.6 s · 크리 1.0 s · 1.6배 · 합산 윈도우 0.25 s」
|
||
// 요소 2 「연쇄 처치 3 s 윈도우 2/3/5/8킬」(윈도우·티어 자체는 811e = WLReactionSettings)
|
||
// 요소 4 「토스트 1.2 s · 동시 3줄 · 등급 색」
|
||
// 요소 5 「레벨업 = 스탯 팝업 3개」
|
||
//
|
||
// ■ 왜 이 에셋인가 (C45 · 발주서 §2 ⓓ)
|
||
// 지속 시간·배율·상한·오프셋을 코드 상수로 두지 않는다. 이 파일에는 "필드와 기본값"만 있고,
|
||
// 게임이 실제로 읽는 값은 Resources/WL/WLCombatTextSettings.asset 이다.
|
||
//
|
||
// ■ 롤백(C8)
|
||
// 에셋이 없거나 enabled = false 면 네 연출(데미지 리듬 · 연쇄 문구 · 전리품 토스트 · 스탯 팝업)이
|
||
// 전부 무동작으로 떨어지고 HUDDMGUI 는 **원본 값(0.9 s · 스케일 1배 · 상한 없음)** 으로 돈다.
|
||
//
|
||
// ■ 좌표계 (px)
|
||
// px 값은 세로 1080×1920 기준 화면 픽셀이다(813c WLHudLayoutSettings 와 같은 규약).
|
||
// px → 캔버스 유닛 환산은 런타임에 캔버스 폭에서 구한다(802b 노트 · 세로에서 1 px = 1.7778 유닛).
|
||
//
|
||
// 🔴 어셈블리 주의: Assets/WL/UI/ 에 .asmdef 를 만들지 말 것(Assembly-CSharp 단일).
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
|
||
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
|
||
namespace WL.UI
|
||
{
|
||
[CreateAssetMenu(fileName = "WLCombatTextSettings", menuName = "WL/Combat Text Settings", order = 31)]
|
||
public sealed class WLCombatTextSettings : ScriptableObject
|
||
{
|
||
/// <summary>Resources 경로 — 런타임 로드 키.</summary>
|
||
public const string ResourcesPath = "WL/WLCombatTextSettings";
|
||
|
||
private static WLCombatTextSettings s_instance;
|
||
private static bool s_tried;
|
||
|
||
/// <summary>에셋이 없으면 null(호출부는 null 이면 원본 동작 유지 = C8 롤백).</summary>
|
||
public static WLCombatTextSettings Instance
|
||
{
|
||
get
|
||
{
|
||
if (s_instance == null && !s_tried)
|
||
{
|
||
s_tried = true;
|
||
s_instance = Resources.Load<WLCombatTextSettings>(ResourcesPath);
|
||
}
|
||
return s_instance;
|
||
}
|
||
}
|
||
|
||
/// <summary>에디터에서 에셋을 새로 만들거나 값을 바꾼 뒤 캐시를 버린다.</summary>
|
||
public static void ClearCache() { s_instance = null; s_tried = false; }
|
||
|
||
/// <summary>진단·A/B 전용 런타임 스위치(에셋을 건드리지 않고 끈다).</summary>
|
||
public static bool RuntimeDisabled;
|
||
|
||
/// <summary>이 웨이브의 텍스트 연출이 살아 있는가 = 에셋 존재 + enabled + 런타임 스위치.</summary>
|
||
public static bool Enabled { get { var i = Instance; return i != null && i.textEnabled && !RuntimeDisabled; } }
|
||
|
||
// ── 전체 스위치 ────────────────────────────────────────────────────────
|
||
[Header("전체 스위치")]
|
||
[Tooltip("끄면 데미지 리듬 · 연쇄 문구 · 전리품 토스트 · 스탯 팝업이 전부 무동작(원본 HUDDMGUI 동작으로 복귀). 롤백 1순위.")]
|
||
public bool textEnabled = true;
|
||
|
||
[Tooltip("표시 판정을 Console 에 남긴다(검증용 · GC 발생).")]
|
||
public bool verboseLog = false;
|
||
|
||
[Header("기준 해상도 (px · 813c 와 동일 규약)")]
|
||
[Tooltip("모든 px 값의 기준이 되는 화면 폭")]
|
||
public float designWidthPx = 1080f;
|
||
[Tooltip("모든 px 값의 기준이 되는 화면 높이")]
|
||
public float designHeightPx = 1920f;
|
||
|
||
// ── ① 데미지 숫자 리듬 (기준서 §B 요소 2) ──────────────────────────────
|
||
[Header("① 데미지 숫자 — 리듬 (HUDDMGUI · 원본 0.9 s · ±25 px 랜덤)")]
|
||
[Tooltip("리듬화 사용 여부. off 면 HUDDMGUI 가 원본 상수(0.9 s · 배율 1)로 돈다.")]
|
||
public bool damageRhythmEnabled = true;
|
||
|
||
[Tooltip("일반 타격 숫자 표시 시간(초). 기준서 §B = 0.6 s.")]
|
||
public float damageNormalSeconds = 0.6f;
|
||
|
||
[Tooltip("크리티컬 숫자 표시 시간(초). 기준서 §B = 1.0 s.")]
|
||
public float damageCritSeconds = 1.0f;
|
||
|
||
[Tooltip("크리티컬 글자 크기 배율. 기준서 §B = 1.6배(일반 = 1.0).")]
|
||
public float damageCritScale = 1.6f;
|
||
|
||
[Tooltip("일반 타격 글자 크기 배율(기준 1.0 · 숫자죽 방지로 낮출 수 있다).")]
|
||
public float damageNormalScale = 1.0f;
|
||
|
||
[Tooltip("화면에 동시에 떠 있을 수 있는 데미지 숫자 상한. 초과하면 가장 오래된 것부터 즉시 회수한다. 0 이하 = 상한 없음.")]
|
||
public int damageMaxConcurrent = 12;
|
||
|
||
[Tooltip("상한 초과로 회수할 때 크리티컬은 남기고 일반부터 회수한다.")]
|
||
public bool damageEvictNormalFirst = true;
|
||
|
||
[Tooltip("위로 떠오르는 속도(원본 1.0 · 캔버스 유닛/초).")]
|
||
public float damageRiseSpeed = 1.0f;
|
||
|
||
[Tooltip("크리티컬은 이 배율로 더 빨리 떠오른다(1 = 일반과 같음).")]
|
||
public float damageCritRiseMultiplier = 1.35f;
|
||
|
||
[Tooltip("표시 시간 대비 이 비율이 지난 뒤부터 알파가 빠지기 시작한다(0~1 · 원본은 처음부터 서서히).")]
|
||
public float damageFadeStartRatio = 0.55f;
|
||
|
||
[Tooltip("랜덤 산포 반경(px · 원본 ±25). 0 이면 정확히 대상 위. 겹침을 줄이려면 키운다.")]
|
||
public float damageScatterPx = 25f;
|
||
|
||
[Tooltip("크리티컬은 산포를 이 배율로 줄여 중앙에 가깝게 띄운다(눈에 잘 띄게).")]
|
||
public float damageCritScatterMultiplier = 0.4f;
|
||
|
||
[Tooltip("811e DamageAggregator.Flushed(합산 0.25 s)를 구독해 '합계 · N타' 를 함께 보여 준다. 데이터는 811e 소유 · 여기는 표시만.")]
|
||
public bool damageUseMergeWindow = true;
|
||
|
||
[Tooltip("합산 표시 형식 — {0} = 합계 · {1} = 타수. 빈 문자열이면 합계만 표시.")]
|
||
public string damageMergeFormat = "{0}";
|
||
|
||
[Tooltip("합산 타수가 이 값 이상일 때만 타수 꼬리표를 붙인다(damageMergeHitsSuffix).")]
|
||
public int damageMergeHitsMin = 2;
|
||
|
||
[Tooltip("타수 꼬리표 형식 — {0} = 타수. 예: \" x{0}\". 빈 문자열이면 붙이지 않는다.")]
|
||
public string damageMergeHitsSuffix = " <size=60%>x{0}</size>";
|
||
|
||
// ── ② 연쇄 처치 문구 (기준서 §B 요소 2 · 데이터는 811e KillChain) ──────
|
||
[Header("② 연쇄 처치 문구 (KillChain.TierReached 구독 · 화면 중앙 상단)")]
|
||
[Tooltip("연쇄 처치 문구 표시 여부.")]
|
||
public bool killChainTextEnabled = true;
|
||
|
||
[Tooltip("문구 표시 시간(초 · unscaled). 발주서 §1-1 = 0.8 s.")]
|
||
public float killChainSeconds = 0.8f;
|
||
|
||
[Tooltip("스케일 펀치 — 등장 순간의 배율(1.0 이면 펀치 없음).")]
|
||
public float killChainPunchScale = 1.45f;
|
||
|
||
[Tooltip("펀치가 1.0 으로 돌아오는 시간(초). 표시 시간보다 짧아야 한다.")]
|
||
public float killChainPunchSeconds = 0.18f;
|
||
|
||
[Tooltip("표시 시간 대비 이 비율이 지난 뒤부터 알파가 빠진다(0~1).")]
|
||
public float killChainFadeStartRatio = 0.6f;
|
||
|
||
[Tooltip("화면 중앙 기준 위쪽 오프셋(px · 세로 1080×1920 기준). Safe Area 안에서 잰다.")]
|
||
public float killChainTopOffsetPx = 520f;
|
||
|
||
[Tooltip("글자 크기(px · 세로 1080×1920 기준).")]
|
||
public float killChainFontPx = 96f;
|
||
|
||
[Tooltip("티어 인덱스별 글자 색(tiers 순서 = DOUBLE/TRIPLE/RAMPAGE/MASSACRE). 모자라면 마지막 색을 쓴다.")]
|
||
public Color[] killChainTierColors = new Color[]
|
||
{
|
||
new Color(1f, 0.94f, 0.72f, 1f),
|
||
new Color(1f, 0.80f, 0.35f, 1f),
|
||
new Color(1f, 0.52f, 0.22f, 1f),
|
||
new Color(1f, 0.27f, 0.28f, 1f),
|
||
};
|
||
|
||
[Tooltip("티어 인덱스별 글자 크기 배율(위 색과 같은 순서). 모자라면 마지막 값.")]
|
||
public float[] killChainTierScales = new float[] { 1f, 1.12f, 1.28f, 1.45f };
|
||
|
||
[Tooltip("문구 형식 — {0} = 티어 이름(811e 에셋의 tiers[i].name) · {1} = 연쇄 킬 수. PD Q5 전까지 임시 영문(발주서 §1-1).")]
|
||
public string killChainFormat = "{0}!";
|
||
|
||
[Tooltip("연쇄 킬 수를 아래 줄에 덧붙인다(빈 문자열이면 표시 안 함) — {0} = 킬 수.")]
|
||
public string killChainSubFormat = "<size=45%>{0} KILLS</size>";
|
||
|
||
// ── ③ 전리품 토스트 (기준서 §B 요소 4) ────────────────────────────────
|
||
[Header("③ 전리품 토스트 (획득 아이템 · 우측 · 등급 색)")]
|
||
[Tooltip("토스트 표시 여부.")]
|
||
public bool lootToastEnabled = true;
|
||
|
||
[Tooltip("한 줄이 남아 있는 시간(초). 기준서 §B 요소 4 = 1.2 s.")]
|
||
public float lootToastSeconds = 1.2f;
|
||
|
||
[Tooltip("동시에 보이는 줄 수. 기준서 §B 요소 4 = 3줄. 넘치면 가장 오래된 줄부터 밀려난다.")]
|
||
public int lootToastLines = 3;
|
||
|
||
[Tooltip("같은 아이템이 이 시간(초) 안에 또 들어오면 새 줄 대신 기존 줄의 수량을 올린다(줄 폭주 방지). 발주서 WL-813c2 §1-4 = 0.5 s.")]
|
||
public float lootToastMergeSeconds = 0.5f;
|
||
|
||
// ── WL-813c2 (Q6 D-31) — 획득 21건에 줄이 밀린다 ───────────────────────
|
||
// 실측: 화면 줄 수는 이미 `lootToastLines`(3)로 막혀 있고, Q6 의 「창 안 6줄」은
|
||
// 프로브가 **표시 시간 창 안의 Picked 이벤트 수**를 센 값이다(`WL813_ScoreProbe.cs:437~443`).
|
||
// 실물 결함은 넘칠 때 오래된 줄이 **페이드 없이 즉시 교체**돼 깜빡이는 것 → 큐 + 즉시 페이드.
|
||
[Tooltip("같은 아이템이 **아직 보이는 줄**로 남아 있으면 병합 창(lootToastMergeSeconds)과 무관하게 수량만 올린다. " +
|
||
"끄면 병합 창 안일 때만 병합한다(813g 동작).")]
|
||
public bool lootToastMergeWhileVisible = true;
|
||
|
||
[Tooltip("줄이 꽉 찼을 때 가장 오래된 줄을 이 시간(초) 안에 **페이드아웃**시키고 새 줄은 큐에서 기다린다. " +
|
||
"0 이면 813g 처럼 즉시 한 칸씩 당겨 교체한다(= C8 롤백).")]
|
||
public float lootToastOverflowFadeSec = 0.15f;
|
||
|
||
[Tooltip("넘쳐서 대기시킬 수 있는 최대 건수(고정 크기 링 · 할당 0). 넘으면 가장 오래 기다린 항목을 버린다.")]
|
||
public int lootToastQueueMax = 6;
|
||
|
||
[Tooltip("우측 여백(px · Safe Area 우측 기준).")]
|
||
public float lootToastRightMarginPx = 40f;
|
||
|
||
[Tooltip("첫 줄의 화면 세로 중앙 기준 오프셋(px · 양수 = 위).")]
|
||
public float lootToastCenterOffsetPx = 120f;
|
||
|
||
[Tooltip("줄 간격(px).")]
|
||
public float lootToastLineHeightPx = 54f;
|
||
|
||
[Tooltip("줄 글자 크기(px).")]
|
||
public float lootToastFontPx = 40f;
|
||
|
||
[Tooltip("줄 폭(px) — 오른쪽 정렬 기준의 상자 폭.")]
|
||
public float lootToastWidthPx = 520f;
|
||
|
||
[Tooltip("표시 시간 대비 이 비율이 지난 뒤부터 알파가 빠진다(0~1).")]
|
||
public float lootToastFadeStartRatio = 0.65f;
|
||
|
||
[Tooltip("등장할 때 오른쪽에서 밀려 들어오는 거리(px · 0 이면 슬라이드 없음).")]
|
||
public float lootToastSlideInPx = 60f;
|
||
|
||
[Tooltip("슬라이드가 끝나는 시간(초).")]
|
||
public float lootToastSlideSeconds = 0.14f;
|
||
|
||
[Tooltip("줄 형식 — {0} = 등급 색 태그 + 이름 · {1} = 개수. 개수 1 이면 lootToastFormatSingle 을 쓴다.")]
|
||
public string lootToastFormat = "{0}[-] x{1}";
|
||
|
||
[Tooltip("개수 1 일 때의 줄 형식 — {0} = 등급 색 태그 + 이름.")]
|
||
public string lootToastFormatSingle = "{0}[-]";
|
||
|
||
[Tooltip("이 등급 이상만 토스트로 띄운다(기준서 §B 요소 4 = 연출은 4 이상 · 토스트는 전부 보고 싶으면 1).")]
|
||
public int lootToastMinGrade = 1;
|
||
|
||
[Tooltip("골드(아이템 ID 2)도 토스트로 띄운다.")]
|
||
public bool lootToastIncludeGold = false;
|
||
|
||
[Tooltip("획득 집계를 초당 몇 번 확인할지(폴링 · DropItemInfo.m_StageDropData 델타). 813f 이벤트가 오면 이 폴링은 걷어낸다.")]
|
||
public float lootToastPollHz = 10f;
|
||
|
||
[Tooltip("등급 색 팔레트(index 0 = 등급 1). 비우면 MyValue.Get_GradeColor(코드 팔레트)를 그대로 쓴다.")]
|
||
public Color[] lootToastGradeColors = new Color[0];
|
||
|
||
// ─────────────────────────────────────── 전리품 토스트 Safe Area 클램프 (Q4-a D-7 · WL-813z)
|
||
// 실측 원인: 줄이 등장할 때 x 가 +lootToastSlideInPx(60) 에서 0 으로 온다. 줄 뿌리는 오른쪽 가장자리에서
|
||
// lootToastRightMarginPx(40) 안쪽에 있으므로 등장 프레임의 오른쪽 끝은 −40 + 60 = **+20 px**,
|
||
// 뒤 패널(lootToastPanelPadXPx = 18)까지 더하면 **+38 px** 가 Safe Area 밖으로 나간다
|
||
// (0.14 s 동안 · Q4-a 캡처 `23_arena_12.png`·`29_levelup_01.png` 의 잘린 `[-]` 조각과 일치).
|
||
[Header("전리품 토스트 Safe Area 클램프 (Q4-a D-7 · WL-813z)")]
|
||
[Tooltip("끄면 813y 배치 100%(C8 롤백 = 0). 켜면 폭·오른쪽 여백·슬라이드 시작 x 를 Safe Area 안으로 자른다.")]
|
||
public bool lootToastClampToSafeArea = true;
|
||
|
||
[Tooltip("줄 폭 상한(px · 0 이면 상한 없음). 실제 폭 = min(lootToastWidthPx, 이 값, 부모 폭 − 좌우 여백).")]
|
||
public float lootToastMaxWidthPx = 560f;
|
||
|
||
[Tooltip("Safe Area 안쪽으로 더 확보할 여백(px). 뒤 패널 여백(lootToastPanelPadXPx)은 여기에 자동으로 더해진다.")]
|
||
public float lootToastSafePadPx = 8f;
|
||
|
||
[Tooltip("이름이 폭을 넘으면 줄바꿈 대신 말줄임(…)으로 자른다(TMP textWrappingMode=NoWrap · overflowMode=Ellipsis).")]
|
||
public bool lootToastEllipsis = true;
|
||
|
||
// ── ④ 레벨업 스탯 팝업 (기준서 §B 요소 5) ─────────────────────────────
|
||
[Header("④ 레벨업 스탯 팝업 (CombatEvents.LevelUp 구독 · 스탯 3줄)")]
|
||
[Tooltip("스탯 팝업 표시 여부.")]
|
||
public bool statPopupEnabled = true;
|
||
|
||
[Tooltip("팝업이 남아 있는 시간(초 · unscaled).")]
|
||
public float statPopupSeconds = 2.0f;
|
||
|
||
[Tooltip("등장 스케일 펀치(1.0 이면 없음).")]
|
||
public float statPopupPunchScale = 1.3f;
|
||
|
||
[Tooltip("펀치가 1.0 으로 돌아오는 시간(초).")]
|
||
public float statPopupPunchSeconds = 0.22f;
|
||
|
||
[Tooltip("표시 시간 대비 이 비율이 지난 뒤부터 알파가 빠진다(0~1).")]
|
||
public float statPopupFadeStartRatio = 0.7f;
|
||
|
||
[Tooltip("화면 중앙 기준 위쪽 오프셋(px).")]
|
||
public float statPopupCenterOffsetPx = 180f;
|
||
|
||
[Tooltip("제목 글자 크기(px).")]
|
||
public float statPopupTitleFontPx = 72f;
|
||
|
||
[Tooltip("스탯 줄 글자 크기(px).")]
|
||
public float statPopupRowFontPx = 46f;
|
||
|
||
[Tooltip("스탯 줄 간격(px).")]
|
||
public float statPopupRowHeightPx = 56f;
|
||
|
||
[Tooltip("보여 줄 스탯 줄 수 상한. 기준서 §B 요소 5 = 3개.")]
|
||
public int statPopupRows = 3;
|
||
|
||
[Tooltip("보여 줄 스탯 종류(순서대로 · statPopupRows 개까지). 813k 가 이전/새 값을 주면 그 값을, 아니면 이 스탯의 스냅샷 차이를 쓴다.")]
|
||
public eStat[] statPopupStats = new eStat[] { eStat.STR, eStat.DEX, eStat.INT };
|
||
|
||
[Tooltip("증가분이 0 인 스탯 줄도 보여 준다.")]
|
||
public bool statPopupShowZero = false;
|
||
|
||
[Tooltip("제목 형식 — {0} = 새 레벨.")]
|
||
public string statPopupTitleFormat = "LEVEL {0}";
|
||
|
||
[Tooltip("스탯 줄 형식 — {0} = 스탯 이름 · {1} = 증가분 · {2} = 새 값.")]
|
||
public string statPopupRowFormat = "{0} +{1}";
|
||
|
||
[Tooltip("제목 색.")]
|
||
public Color statPopupTitleColor = new Color(1f, 0.90f, 0.45f, 1f);
|
||
|
||
[Tooltip("스탯 줄 색.")]
|
||
public Color statPopupRowColor = new Color(0.55f, 0.95f, 0.62f, 1f);
|
||
|
||
[Tooltip("813k GrowthEvents.LevelUp(스탯 3종 + 잔여 포인트)을 구독한다. 끄면 811b 코어 LevelUp(레벨만)만 본다.")]
|
||
public bool statPopupUseGrowthEvents = true;
|
||
|
||
[Tooltip("스탯 변화가 0 일 때(원본은 레벨업으로 스탯을 안 올린다 · 813k ①) 대신 보여 줄 줄 — {0} = 잔여 스탯 포인트. 빈 문자열이면 표시 안 함.")]
|
||
public string statPopupPointFormat = "+5 포인트 <size=70%>(보유 {0})</size>";
|
||
|
||
[Tooltip("한 번의 Add_Exp 에서 여러 레벨이 오를 때(levelsGained ≥ 2) 마지막 레벨만 팝업한다.")]
|
||
public bool statPopupMergeMultiLevel = true;
|
||
|
||
|
||
[Header("⑤ 텍스트 아웃라인 · 그림자 (Q3 결함 D-6 · 밝은 배경 대비 · 포스트 0)")]
|
||
[Tooltip("끄면 아래 4종이 전부 원본 머티리얼로 돌아간다(C8 롤백).")]
|
||
public bool textEdgeEnabled = true;
|
||
|
||
[Tooltip("연쇄 처치 문구(KillChainText) 테두리.")]
|
||
public WLTextEdge killChainEdge = new WLTextEdge
|
||
{
|
||
enabled = true,
|
||
outlineWidth = 0.30f,
|
||
outlineColor = new Color(0.05f, 0.03f, 0.02f, 1f),
|
||
shadowOffset = new Vector2(0.55f, -0.55f),
|
||
shadowColor = new Color(0f, 0f, 0f, 0.8f),
|
||
shadowSoftness = 0.25f,
|
||
faceDilate = 0.1f,
|
||
};
|
||
|
||
[Tooltip("레벨업 스탯 팝업(StatPopup) 테두리.")]
|
||
public WLTextEdge statPopupEdge = new WLTextEdge
|
||
{
|
||
enabled = true,
|
||
outlineWidth = 0.26f,
|
||
outlineColor = new Color(0.05f, 0.03f, 0.02f, 1f),
|
||
shadowOffset = new Vector2(0.45f, -0.45f),
|
||
shadowColor = new Color(0f, 0f, 0f, 0.75f),
|
||
shadowSoftness = 0.2f,
|
||
faceDilate = 0.08f,
|
||
};
|
||
|
||
[Tooltip("데미지 숫자(HUDDMGUI) 테두리. 숫자가 작아 두께는 낮게.")]
|
||
public WLTextEdge damageEdge = new WLTextEdge
|
||
{
|
||
enabled = true,
|
||
outlineWidth = 0.22f,
|
||
outlineColor = new Color(0.03f, 0.02f, 0.02f, 1f),
|
||
shadowOffset = new Vector2(0.35f, -0.35f),
|
||
shadowColor = new Color(0f, 0f, 0f, 0.7f),
|
||
shadowSoftness = 0.15f,
|
||
faceDilate = 0.05f,
|
||
};
|
||
|
||
[Tooltip("전리품 토스트(LootToast) 테두리.")]
|
||
public WLTextEdge lootToastEdge = new WLTextEdge
|
||
{
|
||
enabled = true,
|
||
outlineWidth = 0.24f,
|
||
outlineColor = new Color(0.03f, 0.02f, 0.02f, 1f),
|
||
shadowOffset = new Vector2(0.4f, -0.4f),
|
||
shadowColor = new Color(0f, 0f, 0f, 0.7f),
|
||
shadowSoftness = 0.18f,
|
||
faceDilate = 0.06f,
|
||
};
|
||
|
||
|
||
[Header("⑥ 전리품 토스트 가시성 (Q3 결함 D-6 ② · 15회 발생 · 육안 실패)")]
|
||
[Tooltip("줄 뒤에 반투명 패널을 깐다(밝은 배경 대비 확보 · Image 1장/줄).")]
|
||
public bool lootToastPanelEnabled = true;
|
||
|
||
[Tooltip("패널 색(알파 포함).")]
|
||
public Color lootToastPanelColor = new Color(0.03f, 0.03f, 0.05f, 0.68f);
|
||
|
||
[Tooltip("패널이 글자 좌우로 넘치는 여백(px).")]
|
||
public float lootToastPanelPadXPx = 18f;
|
||
|
||
[Tooltip("패널이 글자 위아래로 넘치는 여백(px).")]
|
||
public float lootToastPanelPadYPx = 6f;
|
||
|
||
[Tooltip("등급 1(회색) 처럼 어두운 등급 색을 이 색으로 대체한다(알파 0 이면 원래 등급 색 유지).")]
|
||
public Color lootToastMinBrightColor = new Color(0.95f, 0.96f, 0.98f, 1f);
|
||
|
||
[Tooltip("등급 색의 밝기(V)가 이 값보다 어두우면 위 색으로 바꾼다(0~1 · 0 이면 대체 안 함).")]
|
||
[Range(0f, 1f)] public float lootToastMinBrightness = 0.72f;
|
||
|
||
// ── 파생값 헬퍼 (코드 상수 0) ──────────────────────────────────────────
|
||
|
||
/// <summary>
|
||
/// px → 캔버스 유닛 계수. 813c(WLHudLayoutSettings.UnitsPerPx)와 **같은 산식**을 쓴다 —
|
||
/// CanvasScaler 설정을 재현해 구하고(에디트 모드처럼 캔버스가 레이아웃 전이어도 값이 나온다),
|
||
/// 스케일러가 없으면 캔버스 rect 폭으로 떨어진다. 세로 1080×1920 · 참조 1920×1080 Expand = 1.7778(802b 실측).
|
||
/// </summary>
|
||
public float UnitsPerPx(Canvas canvas)
|
||
{
|
||
if (designWidthPx <= 0f || designHeightPx <= 0f) return 1f;
|
||
|
||
var scaler = canvas != null ? canvas.GetComponent<CanvasScaler>() : null;
|
||
if (scaler != null && scaler.uiScaleMode == CanvasScaler.ScaleMode.ScaleWithScreenSize)
|
||
{
|
||
Vector2 r = scaler.referenceResolution;
|
||
if (r.x > 0f && r.y > 0f)
|
||
{
|
||
float w = designWidthPx, h = designHeightPx, scale;
|
||
switch (scaler.screenMatchMode)
|
||
{
|
||
case CanvasScaler.ScreenMatchMode.Expand: scale = Mathf.Min(w / r.x, h / r.y); break;
|
||
case CanvasScaler.ScreenMatchMode.Shrink: scale = Mathf.Max(w / r.x, h / r.y); break;
|
||
default: scale = Mathf.Pow(w / r.x, 1f - scaler.matchWidthOrHeight) *
|
||
Mathf.Pow(h / r.y, scaler.matchWidthOrHeight); break;
|
||
}
|
||
if (scale > 0f) return (w / scale) / designWidthPx;
|
||
}
|
||
}
|
||
|
||
var canvasRect = canvas != null ? canvas.transform as RectTransform : null;
|
||
if (canvasRect != null && canvasRect.rect.width > 0f) return canvasRect.rect.width / designWidthPx;
|
||
return 1f;
|
||
}
|
||
|
||
/// <summary>티어 인덱스의 색(배열이 짧으면 마지막 색 · 비어 있으면 흰색).</summary>
|
||
public Color TierColor(int tier)
|
||
{
|
||
if (killChainTierColors == null || killChainTierColors.Length == 0) return Color.white;
|
||
return killChainTierColors[Mathf.Clamp(tier, 0, killChainTierColors.Length - 1)];
|
||
}
|
||
|
||
/// <summary>티어 인덱스의 크기 배율(배열이 짧으면 마지막 값 · 비어 있으면 1).</summary>
|
||
public float TierScale(int tier)
|
||
{
|
||
if (killChainTierScales == null || killChainTierScales.Length == 0) return 1f;
|
||
return killChainTierScales[Mathf.Clamp(tier, 0, killChainTierScales.Length - 1)];
|
||
}
|
||
|
||
/// <summary>등급(1~9) 색. 팔레트가 비어 있으면 MyValue 코드 팔레트를 파싱해 쓴다.</summary>
|
||
public Color GradeColor(int grade)
|
||
{
|
||
if (lootToastGradeColors != null && lootToastGradeColors.Length > 0)
|
||
return lootToastGradeColors[Mathf.Clamp(grade - 1, 0, lootToastGradeColors.Length - 1)];
|
||
Color c;
|
||
var tag = MyValue.Get_GradeColor(grade); // "<color=#a6a6a7>"
|
||
int s = tag.IndexOf('#');
|
||
int e = tag.IndexOf('>');
|
||
if (s >= 0 && e > s && ColorUtility.TryParseHtmlString(tag.Substring(s, e - s), out c)) return c;
|
||
return Color.white;
|
||
}
|
||
|
||
/// <summary>등급의 리치텍스트 색 태그(원본 팔레트 그대로 · 기준서 §B 요소 4 「보유 팔레트 그대로」).</summary>
|
||
public string GradeColorTag(int grade) { return MyValue.Get_GradeColor(grade); }
|
||
|
||
/// <summary>
|
||
/// 밝은 배경에서도 읽히도록 보정한 등급 색 태그 (WL-813y · Q3 결함 D-6 ②).
|
||
/// 등급 1 은 원본 팔레트가 `#a6a6a7`(회색 · V≈0.65) 이라 모래 배경에서 사라진다 —
|
||
/// 밝기 V 가 <see cref="lootToastMinBrightness"/> 미만이면 색상(H)·채도(S)를 유지한 채 V 를 끌어올린다.
|
||
/// 값이 0 이거나 대체 색 알파가 0 이면 **원본 팔레트 그대로**(C8 롤백).
|
||
/// </summary>
|
||
public string GradeColorTagBright(int grade)
|
||
{
|
||
if (lootToastMinBrightness <= 0f || lootToastMinBrightColor.a <= 0f) return GradeColorTag(grade);
|
||
Color c = GradeColor(grade);
|
||
float h, s, v;
|
||
Color.RGBToHSV(c, out h, out s, out v);
|
||
if (v >= lootToastMinBrightness) return GradeColorTag(grade);
|
||
Color lift = s <= 0.05f ? lootToastMinBrightColor : Color.HSVToRGB(h, s, lootToastMinBrightness);
|
||
return "<color=#" + ColorUtility.ToHtmlStringRGB(lift) + ">";
|
||
}
|
||
|
||
/// <summary>전체 스위치(textEdgeEdge)를 반영한 테두리 값. 꺼져 있으면 enabled=false 로 준다.</summary>
|
||
public WLTextEdge Edge(WLTextEdge e)
|
||
{
|
||
if (!textEdgeEnabled) e.enabled = false;
|
||
return e;
|
||
}
|
||
}
|
||
}
|