[WL-813f2] 전리품 등급 하한 백: 엘리트 ≥3 · 보스 ≥5 (#813)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
834c0ac014
commit
ba3527bd6c
|
|
@ -0,0 +1,48 @@
|
||||||
|
═══ WL-813f2 등급 하한 백 프로브 (edit mode) 15:15:04 ═══
|
||||||
|
|
||||||
|
[a] WLLootSettings — enabled=True burstEnabled=True
|
||||||
|
eliteBagId=8130002 eliteFloorDrops=1 bossBagId=8130003
|
||||||
|
eliteBurst=2~3 bossBurst=3~5
|
||||||
|
beamMinGrade=4 burstRollsBag=True burstGoldShare=0.25
|
||||||
|
|
||||||
|
[b] 테이블 부트스트랩
|
||||||
|
부트 전: table_randombag.Ins=False table_itemlist.Ins=False
|
||||||
|
부트 후: table_randombag.Ins=True table_itemlist.Ins=True
|
||||||
|
행 수: 8130001=5 8130002=3 8130003=3 전체=208
|
||||||
|
ResolveGrade 표본: 10101012=3 10101017=4 10101022=5 10101023=5 10101027=6 10101032=7
|
||||||
|
|
||||||
|
[c] RollBag(8130002) ×100 — g3:69(69.0%) g4:21(21.0%) g5:10(10.0%) | 빈손 0
|
||||||
|
최소 등급 3 · 등급 ≥3 100/100 · 빔(≥4) 31/100 (31.0%)
|
||||||
|
|
||||||
|
[d] RollBag(8130003) ×100 — g5:73(73.0%) g6:17(17.0%) g7:10(10.0%) | 빈손 0
|
||||||
|
최소 등급 5 · 등급 ≥5 100/100 · 빔(≥4) 100/100 (100.0%)
|
||||||
|
|
||||||
|
[e] RollBag(8130001) ×4000 — g1:464(11.6%) g2:203(5.1%) g3:74(1.9%) g4:68(1.7%) g5:9(0.2%) | 빈손 3182
|
||||||
|
드랍 818/4000 = 20.45% (기준서 15~25%)
|
||||||
|
드랍 조건부 분포: g1 56.7% · g2 24.8% · g3 9.0% · g4 8.3% · g5 1.1% → 등급 1~2 = 81.5%
|
||||||
|
빔(≥4) 77/4000 = 1.925% (전 처치 대비)
|
||||||
|
|
||||||
|
[f] 엘리트 버스트 ×200 — 총 504건 · 8130002 건수 min 1 max 1 · 0건인 처치 0회 (요구: 0)
|
||||||
|
[g] 보스 버스트 ×200 — 총 802건 · 8130003 이 아닌 건 0 (요구: 0)
|
||||||
|
일반 잡몹: BagForBurst(None,0,zone)=8130001 · normalBurstCount=0
|
||||||
|
|
||||||
|
[h] 가짜 Killed 3건 — core Enabled=True 구독=True 처리=3 · elite 계획 2 · boss 3 · none 0 · 누적 BurstPlanned 5 · 실드랍 0(DropItemInfo.Ins=False)
|
||||||
|
|
||||||
|
[i] GC — BagForBurst ×1000 델타 0 B · RollBag ×1000 델타 0 B
|
||||||
|
|
||||||
|
[j] C8 — eliteBagId/bossBagId 0 → Elite i0=8130001 Boss i0=8130001 (요구: 둘 다 8130001)
|
||||||
|
복원: eliteBagId=8130002 bossBagId=8130003 (메모리만 · 에셋 저장 0)
|
||||||
|
RuntimeDisabled 경로는 813f 프로브 [j] 에서 이미 실측(훅 전체 무동작)
|
||||||
|
|
||||||
|
[k] Assets/ResWork/Table/Export/RandomBag.json
|
||||||
|
총 행 208 · 줄바꿈 없음(한 줄 JSON)
|
||||||
|
새 백 원문 시작 오프셋 30114 / 길이 31019 (= 뒤에 붙임 · 앞 행 무변경)
|
||||||
|
8130002/8130003 원문:
|
||||||
|
{"n_DropIndex": "8130002", "n_ItemGrade": "5", "e_ItemType": "Weapon", "n_ItemId": "10101022", "f_DropRate": "0.1", "n_DropCount": "1", "exception": ""}
|
||||||
|
{"n_DropIndex": "8130002", "n_ItemGrade": "4", "e_ItemType": "Weapon", "n_ItemId": "10101017", "f_DropRate": "0.3", "n_DropCount": "1", "exception": ""}
|
||||||
|
{"n_DropIndex": "8130002", "n_ItemGrade": "3", "e_ItemType": "Weapon", "n_ItemId": "10101012", "f_DropRate": "1", "n_DropCount": "1", "exception": ""}
|
||||||
|
{"n_DropIndex": "8130003", "n_ItemGrade": "7", "e_ItemType": "Weapon", "n_ItemId": "10101032", "f_DropRate": "0.08", "n_DropCount": "1", "exception": ""}
|
||||||
|
{"n_DropIndex": "8130003", "n_ItemGrade": "6", "e_ItemType": "Weapon", "n_ItemId": "10101027", "f_DropRate": "0.25", "n_DropCount": "1", "exception": ""}
|
||||||
|
{"n_DropIndex": "8130003", "n_ItemGrade": "5", "e_ItemType": "Weapon", "n_ItemId": "10101023", "f_DropRate": "1", "n_DropCount": "1", "exception": ""}
|
||||||
|
|
||||||
|
═══ 종료 · 부트한 테이블 회수 완료(Ins=null · 호스트 GO 파괴) ═══
|
||||||
|
|
@ -0,0 +1,323 @@
|
||||||
|
// 발주서 WL-813f2 — 전리품 등급 하한 백(엘리트 ≥3 · 보스 ≥5) 검증 프로브
|
||||||
|
//
|
||||||
|
// 실행 (CLAUDE.md §1: using 금지 · 네임스페이스 풀어 쓰기)
|
||||||
|
// 에디트 모드 : unity command run_script --file AgentScripts/WL813f2_Probe.cs --entry WL813f2_Probe.Edit --timeout 600
|
||||||
|
//
|
||||||
|
// Edit() 가 재는 것 — 씬/프리팹/에셋 변경 0(가짜 오브젝트·가짜 테이블은 전부 회수):
|
||||||
|
// (a) WLLootSettings 값 — eliteBagId · eliteFloorDrops · bossBagId · beamMinGrade
|
||||||
|
// (b) 테이블 부트스트랩 — table_randombag / table_itemlist 를 TextAsset 에서 에디트 모드에 실제로 올린다
|
||||||
|
// (813f 는 싱글턴이 없어 백 추첨을 못 쟀다. 여기서는 비활성 GO + 리플렉션 Awake/Start 로 실제 경로를 태운다)
|
||||||
|
// (c) RollBag(8130002) ×100 → ResolveGrade 등급 분포 · 등급 ≥3 비율 · 빈손 횟수
|
||||||
|
// (d) RollBag(8130003) ×100 → 등급 ≥5 비율 · 빔(BeamPrefab) 뜨는 비율
|
||||||
|
// (e) RollBag(8130001) ×N → 존 잡몹 백 등급 분포(등급 1~2 비율) — 기준서 §E 4-1 「등급 색 구분」 근거
|
||||||
|
// (f) 엘리트 버스트 시뮬 ×200 — BurstCount(Elite) 건수마다 BagForBurst → 8130002 가 정확히 몇 건인가
|
||||||
|
// (g) 보스 버스트 시뮬 ×200 — 전 건 8130003 인가
|
||||||
|
// (h) 가짜 Killed(Elite/Boss) → 구독·BurstPlanned 동작
|
||||||
|
// (i) GC — RollBag 1000회 · BagForBurst 1000회 할당 바이트
|
||||||
|
// (j) C8 — eliteBagId/bossBagId 0 이면 존 백 그대로(기존 동작)
|
||||||
|
// (k) 데이터 — RandomBag 행 수 · 새 백 2개 행 전수 · 기존 202행 무변경 확인
|
||||||
|
//
|
||||||
|
// 수치는 전부 SO/테이블에서 읽는다(C45). 프로브 상수는 "어디를 보나" 뿐이다.
|
||||||
|
|
||||||
|
public static class WL813f2_Probe
|
||||||
|
{
|
||||||
|
const string kBagJson = "Assets/ResWork/Table/Export/RandomBag.json";
|
||||||
|
const string kItemJson = "Assets/ResWork/Table/Export/ItemList.json";
|
||||||
|
const string kOutDir = "AgentScripts/staging/WL813f2";
|
||||||
|
const int kZoneBag = 8130001;
|
||||||
|
const int kEliteBag = 8130002;
|
||||||
|
const int kBossBag = 8130003;
|
||||||
|
const int kRolls = 100;
|
||||||
|
|
||||||
|
static System.Text.StringBuilder s;
|
||||||
|
static void L(string t) { s.AppendLine(t); UnityEngine.Debug.Log("[WL813f2] " + t); }
|
||||||
|
|
||||||
|
static string Abs(string rel)
|
||||||
|
{
|
||||||
|
return System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(),
|
||||||
|
rel.Replace('/', System.IO.Path.DirectorySeparatorChar));
|
||||||
|
}
|
||||||
|
|
||||||
|
static string Flush(string name)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
System.IO.Directory.CreateDirectory(Abs(kOutDir));
|
||||||
|
System.IO.File.WriteAllText(Abs(kOutDir + "/" + name), s.ToString(), new System.Text.UTF8Encoding(true));
|
||||||
|
}
|
||||||
|
catch (System.Exception e) { UnityEngine.Debug.LogWarning("[WL813f2] 로그 저장 실패: " + e.Message); }
|
||||||
|
return s.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 테이블 부트스트랩 ────────────────────────────────────────────────────
|
||||||
|
// table_missionbase.Awake() 가 json_last = m_json.text 를 읽고, 파생 Start() 가 역직렬화한다.
|
||||||
|
// 에디트 모드에서는 Awake/Start 가 자동으로 안 불리므로 비활성 GO 에 붙이고 리플렉션으로 직접 부른다.
|
||||||
|
static UnityEngine.GameObject s_tableHost;
|
||||||
|
|
||||||
|
// Awake 는 m_json(TextAsset · 인스펙터 참조)을 읽으므로 태우지 않는다.
|
||||||
|
// 대신 protected json_last 에 파일 본문을 직접 넣고 Ins 를 세운 뒤 Start() 만 실제로 호출한다.
|
||||||
|
static T Boot<T>(string jsonPath) where T : UnityEngine.MonoBehaviour
|
||||||
|
{
|
||||||
|
if (s_tableHost == null)
|
||||||
|
{
|
||||||
|
s_tableHost = new UnityEngine.GameObject("WL813f2_TableHost");
|
||||||
|
s_tableHost.SetActive(false); // 비활성 = Awake 자동 호출 없음
|
||||||
|
s_tableHost.hideFlags = UnityEngine.HideFlags.HideAndDontSave;
|
||||||
|
}
|
||||||
|
var comp = s_tableHost.AddComponent<T>();
|
||||||
|
|
||||||
|
var fj = typeof(table_missionbase).GetField("json_last",
|
||||||
|
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||||
|
if (fj == null) { L(" ! json_last 필드 없음"); return null; }
|
||||||
|
fj.SetValue(comp, System.IO.File.ReadAllText(Abs(jsonPath))); // BOM 은 StreamReader 가 벗긴다
|
||||||
|
|
||||||
|
var fi = typeof(T).GetField("Ins",
|
||||||
|
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
|
||||||
|
if (fi == null) { L(" ! Ins 정적 필드 없음: " + typeof(T).Name); return null; }
|
||||||
|
fi.SetValue(null, comp);
|
||||||
|
|
||||||
|
Call(comp, "Start");
|
||||||
|
return comp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Call(object o, string name)
|
||||||
|
{
|
||||||
|
var m = o.GetType().GetMethod(name, System.Reflection.BindingFlags.Instance |
|
||||||
|
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public);
|
||||||
|
if (m == null) { L(" ! 메서드 없음: " + name); return; }
|
||||||
|
m.Invoke(o, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int BagRows(int bagId)
|
||||||
|
{
|
||||||
|
try { var l = table_randombag.Ins.Get_DataList(bagId); return l == null ? -1 : l.Count; }
|
||||||
|
catch (System.Exception) { return -1; } // Get_DataList 는 없는 백이면 KeyNotFound 를 던진다
|
||||||
|
}
|
||||||
|
|
||||||
|
static Actor MakeFakeMob(string name, eSubRol subRole)
|
||||||
|
{
|
||||||
|
var go = new UnityEngine.GameObject(name);
|
||||||
|
go.hideFlags = UnityEngine.HideFlags.HideAndDontSave;
|
||||||
|
go.SetActive(false); // 비활성 = Awake/Start 안 탐
|
||||||
|
var a = go.AddComponent<MobActor>();
|
||||||
|
a.m_SubRole = subRole;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Teardown()
|
||||||
|
{
|
||||||
|
table_randombag.Ins = null;
|
||||||
|
table_itemlist.Ins = null;
|
||||||
|
if (s_tableHost != null) UnityEngine.Object.DestroyImmediate(s_tableHost);
|
||||||
|
s_tableHost = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 분포 집계 ───────────────────────────────────────────────────────────
|
||||||
|
static string GradeTable(int[] hist, int total, int empty)
|
||||||
|
{
|
||||||
|
var sb = new System.Text.StringBuilder();
|
||||||
|
for (int g = 1; g <= 9; g++)
|
||||||
|
if (hist[g] > 0) sb.Append("g" + g + ":" + hist[g] + "(" + (100f * hist[g] / total).ToString("0.0") + "%) ");
|
||||||
|
sb.Append("| 빈손 " + empty);
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int RollGrades(int bagId, int rolls, int[] hist, out int empty, out int beam)
|
||||||
|
{
|
||||||
|
empty = 0; beam = 0;
|
||||||
|
var st = WL.Combat.Loot.WLLootSettings.Instance;
|
||||||
|
int min = 99;
|
||||||
|
for (int i = 0; i < rolls; i++)
|
||||||
|
{
|
||||||
|
int itemId, amount;
|
||||||
|
if (!WL.Combat.Loot.LootBurst.RollBag(bagId, out itemId, out amount)) { empty++; min = 0; continue; }
|
||||||
|
int g = WL.Combat.Loot.LootBurst.ResolveGrade(itemId);
|
||||||
|
if (g >= 1 && g <= 9) hist[g]++;
|
||||||
|
if (g < min) min = g;
|
||||||
|
if (g >= st.beamMinGrade && WL.Combat.Loot.LootBurst.BeamPrefab(st, g) != null) beam++;
|
||||||
|
}
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Edit()
|
||||||
|
{
|
||||||
|
s = new System.Text.StringBuilder();
|
||||||
|
L("═══ WL-813f2 등급 하한 백 프로브 (edit mode) " + System.DateTime.Now.ToString("HH:mm:ss") + " ═══");
|
||||||
|
|
||||||
|
WL.Combat.Loot.WLLootSettings.ClearCache();
|
||||||
|
var st = WL.Combat.Loot.WLLootSettings.Instance;
|
||||||
|
if (st == null) { L("!! WLLootSettings 로드 실패"); return Flush("EDIT.txt"); }
|
||||||
|
WL.Combat.Loot.LootBurst.EnsureSubscribed();
|
||||||
|
|
||||||
|
// (a) 값
|
||||||
|
L("");
|
||||||
|
L("[a] WLLootSettings — enabled=" + st.enabled + " burstEnabled=" + st.burstEnabled);
|
||||||
|
L(" eliteBagId=" + st.eliteBagId + " eliteFloorDrops=" + st.eliteFloorDrops + " bossBagId=" + st.bossBagId);
|
||||||
|
L(" eliteBurst=" + st.eliteBurstMin + "~" + st.eliteBurstMax + " bossBurst=" + st.bossBurstMin + "~" + st.bossBurstMax);
|
||||||
|
L(" beamMinGrade=" + st.beamMinGrade + " burstRollsBag=" + st.burstRollsBag + " burstGoldShare=" + st.burstGoldShare);
|
||||||
|
|
||||||
|
// (b) 테이블 부트스트랩
|
||||||
|
L("");
|
||||||
|
L("[b] 테이블 부트스트랩");
|
||||||
|
L(" 부트 전: table_randombag.Ins=" + (table_randombag.Ins != null) + " table_itemlist.Ins=" + (table_itemlist.Ins != null));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Boot<table_randombag>(kBagJson);
|
||||||
|
Boot<table_itemlist>(kItemJson);
|
||||||
|
}
|
||||||
|
catch (System.Exception e) { L(" !! 부트 실패: " + e.GetType().Name + " " + e.Message); }
|
||||||
|
bool booted = table_randombag.Ins != null && table_itemlist.Ins != null;
|
||||||
|
L(" 부트 후: table_randombag.Ins=" + (table_randombag.Ins != null) + " table_itemlist.Ins=" + (table_itemlist.Ins != null));
|
||||||
|
if (booted)
|
||||||
|
{
|
||||||
|
L(" 행 수: 8130001=" + BagRows(kZoneBag) + " 8130002=" + BagRows(kEliteBag) + " 8130003=" + BagRows(kBossBag) +
|
||||||
|
" 전체=" + table_randombag.Ins.Get_DataList().Count);
|
||||||
|
L(" ResolveGrade 표본: 10101012=" + WL.Combat.Loot.LootBurst.ResolveGrade(10101012) +
|
||||||
|
" 10101017=" + WL.Combat.Loot.LootBurst.ResolveGrade(10101017) +
|
||||||
|
" 10101022=" + WL.Combat.Loot.LootBurst.ResolveGrade(10101022) +
|
||||||
|
" 10101023=" + WL.Combat.Loot.LootBurst.ResolveGrade(10101023) +
|
||||||
|
" 10101027=" + WL.Combat.Loot.LootBurst.ResolveGrade(10101027) +
|
||||||
|
" 10101032=" + WL.Combat.Loot.LootBurst.ResolveGrade(10101032));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (booted)
|
||||||
|
{
|
||||||
|
// (c) 엘리트 백
|
||||||
|
L("");
|
||||||
|
var h2 = new int[10]; int e2, b2;
|
||||||
|
int min2 = RollGrades(kEliteBag, kRolls, h2, out e2, out b2);
|
||||||
|
L("[c] RollBag(8130002) ×" + kRolls + " — " + GradeTable(h2, kRolls, e2));
|
||||||
|
L(" 최소 등급 " + min2 + " · 등급 ≥3 " + (kRolls - e2 - h2[1] - h2[2]) + "/" + kRolls +
|
||||||
|
" · 빔(≥" + st.beamMinGrade + ") " + b2 + "/" + kRolls + " (" + (100f * b2 / kRolls).ToString("0.0") + "%)");
|
||||||
|
|
||||||
|
// (d) 보스 백
|
||||||
|
L("");
|
||||||
|
var h3 = new int[10]; int e3, b3;
|
||||||
|
int min3 = RollGrades(kBossBag, kRolls, h3, out e3, out b3);
|
||||||
|
int ge5 = h3[5] + h3[6] + h3[7] + h3[8] + h3[9];
|
||||||
|
L("[d] RollBag(8130003) ×" + kRolls + " — " + GradeTable(h3, kRolls, e3));
|
||||||
|
L(" 최소 등급 " + min3 + " · 등급 ≥5 " + ge5 + "/" + kRolls +
|
||||||
|
" · 빔(≥" + st.beamMinGrade + ") " + b3 + "/" + kRolls + " (" + (100f * b3 / kRolls).ToString("0.0") + "%)");
|
||||||
|
|
||||||
|
// (e) 존 잡몹 백 — 색 구분 근거
|
||||||
|
L("");
|
||||||
|
const int kZoneRolls = 4000;
|
||||||
|
var h1 = new int[10]; int e1, b1;
|
||||||
|
RollGrades(kZoneBag, kZoneRolls, h1, out e1, out b1);
|
||||||
|
int dropped = kZoneRolls - e1;
|
||||||
|
L("[e] RollBag(8130001) ×" + kZoneRolls + " — " + GradeTable(h1, kZoneRolls, e1));
|
||||||
|
L(" 드랍 " + dropped + "/" + kZoneRolls + " = " + (100f * dropped / kZoneRolls).ToString("0.00") + "% (기준서 15~25%)");
|
||||||
|
if (dropped > 0)
|
||||||
|
L(" 드랍 조건부 분포: g1 " + (100f * h1[1] / dropped).ToString("0.0") + "% · g2 " + (100f * h1[2] / dropped).ToString("0.0") +
|
||||||
|
"% · g3 " + (100f * h1[3] / dropped).ToString("0.0") + "% · g4 " + (100f * h1[4] / dropped).ToString("0.0") +
|
||||||
|
"% · g5 " + (100f * h1[5] / dropped).ToString("0.0") + "% → 등급 1~2 = " +
|
||||||
|
(100f * (h1[1] + h1[2]) / dropped).ToString("0.0") + "%");
|
||||||
|
L(" 빔(≥" + st.beamMinGrade + ") " + b1 + "/" + kZoneRolls + " = " + (100f * b1 / kZoneRolls).ToString("0.000") + "% (전 처치 대비)");
|
||||||
|
}
|
||||||
|
else L("[c][d][e] 건너뜀 — 테이블 부트 실패");
|
||||||
|
|
||||||
|
// (f)(g) 버스트 시뮬 — 실제 BurstCount + BagForBurst 경로
|
||||||
|
L("");
|
||||||
|
const int kSim = 200;
|
||||||
|
int eliteMinFloor = 99, eliteMaxFloor = 0, eliteZero = 0, eliteTotal = 0;
|
||||||
|
for (int t = 0; t < kSim; t++)
|
||||||
|
{
|
||||||
|
int n = WL.Combat.Loot.LootBurst.BurstCount(st, eSubRol.Elite);
|
||||||
|
eliteTotal += n;
|
||||||
|
int floor = 0;
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
if (WL.Combat.Loot.LootBurst.BagForBurst(st, eSubRol.Elite, i, kZoneBag) == kEliteBag) floor++;
|
||||||
|
if (floor < eliteMinFloor) eliteMinFloor = floor;
|
||||||
|
if (floor > eliteMaxFloor) eliteMaxFloor = floor;
|
||||||
|
if (floor == 0) eliteZero++;
|
||||||
|
}
|
||||||
|
L("[f] 엘리트 버스트 ×" + kSim + " — 총 " + eliteTotal + "건 · 8130002 건수 min " + eliteMinFloor + " max " + eliteMaxFloor +
|
||||||
|
" · 0건인 처치 " + eliteZero + "회 (요구: 0)");
|
||||||
|
|
||||||
|
int bossOther = 0, bossTotal = 0;
|
||||||
|
for (int t = 0; t < kSim; t++)
|
||||||
|
{
|
||||||
|
int n = WL.Combat.Loot.LootBurst.BurstCount(st, eSubRol.Boss);
|
||||||
|
bossTotal += n;
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
if (WL.Combat.Loot.LootBurst.BagForBurst(st, eSubRol.Boss, i, kZoneBag) != kBossBag) bossOther++;
|
||||||
|
}
|
||||||
|
L("[g] 보스 버스트 ×" + kSim + " — 총 " + bossTotal + "건 · 8130003 이 아닌 건 " + bossOther + " (요구: 0)");
|
||||||
|
L(" 일반 잡몹: BagForBurst(None,0,zone)=" + WL.Combat.Loot.LootBurst.BagForBurst(st, eSubRol.None, 0, kZoneBag) +
|
||||||
|
" · normalBurstCount=" + st.normalBurstCount);
|
||||||
|
|
||||||
|
// (h) 가짜 Killed
|
||||||
|
L("");
|
||||||
|
var mobE = MakeFakeMob("__f2Elite", eSubRol.Elite);
|
||||||
|
var mobB = MakeFakeMob("__f2Boss", eSubRol.Boss);
|
||||||
|
var mobN = MakeFakeMob("__f2None", eSubRol.None);
|
||||||
|
WL.Combat.Loot.LootBurst.ResetDiagnostics();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
WL.Combat.Core.CombatEvents.RaiseKilled(mobE);
|
||||||
|
int cE = WL.Combat.Loot.LootBurst.LastBurstCount;
|
||||||
|
WL.Combat.Core.CombatEvents.RaiseKilled(mobB);
|
||||||
|
int cB = WL.Combat.Loot.LootBurst.LastBurstCount;
|
||||||
|
WL.Combat.Core.CombatEvents.RaiseKilled(mobN);
|
||||||
|
int cN = WL.Combat.Loot.LootBurst.LastBurstCount;
|
||||||
|
L("[h] 가짜 Killed 3건 — core Enabled=" + WL.Combat.Core.WLCombatCoreSettings.Enabled +
|
||||||
|
" 구독=" + WL.Combat.Loot.LootBurst.Subscribed + " 처리=" + WL.Combat.Loot.LootBurst.KilledHandled +
|
||||||
|
" · elite 계획 " + cE + " · boss " + cB + " · none " + cN +
|
||||||
|
" · 누적 BurstPlanned " + WL.Combat.Loot.LootBurst.BurstPlanned +
|
||||||
|
" · 실드랍 " + WL.Combat.Loot.LootBurst.BurstDropped + "(DropItemInfo.Ins=" + (DropItemInfo.Ins != null) + ")");
|
||||||
|
}
|
||||||
|
catch (System.Exception e) { L("[h] !! RaiseKilled 실패: " + e.GetType().Name + " " + e.Message); }
|
||||||
|
UnityEngine.Object.DestroyImmediate(mobE.gameObject);
|
||||||
|
UnityEngine.Object.DestroyImmediate(mobB.gameObject);
|
||||||
|
UnityEngine.Object.DestroyImmediate(mobN.gameObject);
|
||||||
|
|
||||||
|
// (i) GC
|
||||||
|
L("");
|
||||||
|
long g0 = System.GC.GetTotalMemory(false);
|
||||||
|
for (int i = 0; i < 1000; i++) WL.Combat.Loot.LootBurst.BagForBurst(st, eSubRol.Elite, i & 3, kZoneBag);
|
||||||
|
long g1 = System.GC.GetTotalMemory(false);
|
||||||
|
long r0 = System.GC.GetTotalMemory(false);
|
||||||
|
if (booted) { int a, b; for (int i = 0; i < 1000; i++) WL.Combat.Loot.LootBurst.RollBag(kEliteBag, out a, out b); }
|
||||||
|
long r1 = System.GC.GetTotalMemory(false);
|
||||||
|
L("[i] GC — BagForBurst ×1000 델타 " + (g1 - g0) + " B · RollBag ×1000 델타 " + (r1 - r0) + " B");
|
||||||
|
|
||||||
|
// (j) C8
|
||||||
|
L("");
|
||||||
|
int se = st.eliteBagId, sb = st.bossBagId;
|
||||||
|
st.eliteBagId = 0; st.bossBagId = 0;
|
||||||
|
L("[j] C8 — eliteBagId/bossBagId 0 → Elite i0=" + WL.Combat.Loot.LootBurst.BagForBurst(st, eSubRol.Elite, 0, kZoneBag) +
|
||||||
|
" Boss i0=" + WL.Combat.Loot.LootBurst.BagForBurst(st, eSubRol.Boss, 0, kZoneBag) + " (요구: 둘 다 " + kZoneBag + ")");
|
||||||
|
st.eliteBagId = se; st.bossBagId = sb;
|
||||||
|
L(" 복원: eliteBagId=" + st.eliteBagId + " bossBagId=" + st.bossBagId + " (메모리만 · 에셋 저장 0)");
|
||||||
|
L(" RuntimeDisabled 경로는 813f 프로브 [j] 에서 이미 실측(훅 전체 무동작)");
|
||||||
|
|
||||||
|
// (k) 데이터 원문
|
||||||
|
L("");
|
||||||
|
L("[k] " + kBagJson);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string txt = System.IO.File.ReadAllText(Abs(kBagJson));
|
||||||
|
int rows = 0;
|
||||||
|
for (int i = 0; i < txt.Length; i++) if (txt[i] == '{') rows++;
|
||||||
|
L(" 총 행 " + rows + " · 줄바꿈 " + (txt.IndexOf('\n') >= 0 ? "있음" : "없음(한 줄 JSON)"));
|
||||||
|
int at = txt.IndexOf("\"8130002\"");
|
||||||
|
if (at > 0) L(" 새 백 원문 시작 오프셋 " + at + " / 길이 " + txt.Length + " (= 뒤에 붙임 · 앞 행 무변경)");
|
||||||
|
L(" 8130002/8130003 원문:");
|
||||||
|
int pos = at;
|
||||||
|
while (pos > 0)
|
||||||
|
{
|
||||||
|
int e = txt.IndexOf('}', pos);
|
||||||
|
if (e < 0) break;
|
||||||
|
L(" " + txt.Substring(txt.LastIndexOf('{', pos), e - txt.LastIndexOf('{', pos) + 1));
|
||||||
|
pos = txt.IndexOf("81300", e);
|
||||||
|
if (pos < 0) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception e) { L(" !! 읽기 실패: " + e.Message); }
|
||||||
|
|
||||||
|
Teardown();
|
||||||
|
L("");
|
||||||
|
L("═══ 종료 · 부트한 테이블 회수 완료(Ins=null · 호스트 GO 파괴) ═══");
|
||||||
|
return Flush("EDIT.txt");
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -223,10 +223,10 @@ namespace WL.Combat.Loot
|
||||||
int baseGold = appear != null ? appear.n_DropGold : 0;
|
int baseGold = appear != null ? appear.n_DropGold : 0;
|
||||||
|
|
||||||
if (Application.isPlaying && s.burstIntervalSeconds > 0f)
|
if (Application.isPlaying && s.burstIntervalSeconds > 0f)
|
||||||
Runner.RunBurst(count, bagId, baseGold, e.position, s.burstIntervalSeconds, held);
|
Runner.RunBurst(count, bagId, baseGold, e.position, s.burstIntervalSeconds, held, e.subRole);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int i = 0; i < count; i++) SpawnBurstOne(bagId, baseGold, e.position);
|
for (int i = 0; i < count; i++) SpawnBurstOne(BagForBurst(s, e.subRole, i, bagId), baseGold, e.position);
|
||||||
if (held) EffectBudget.Release(EffectBudgetKind.ClusterBurst);
|
if (held) EffectBudget.Release(EffectBudgetKind.ClusterBurst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -249,6 +249,18 @@ namespace WL.Combat.Loot
|
||||||
return UnityEngine.Random.Range(min, max + 1);
|
return UnityEngine.Random.Range(min, max + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 이 폭발 드랍 1건이 쓸 백. 보스는 전부 bossBagId(등급 ≥5), 엘리트는 앞 eliteFloorDrops 건만 eliteBagId(등급 ≥3),
|
||||||
|
/// 나머지는 몹의 존 백 그대로. ID 0 = 기존 동작(C8).
|
||||||
|
/// </summary>
|
||||||
|
public static int BagForBurst(WLLootSettings s, eSubRol subRole, int index, int zoneBagId)
|
||||||
|
{
|
||||||
|
if (s == null) return zoneBagId;
|
||||||
|
if (subRole == eSubRol.Boss && s.bossBagId > 0) return s.bossBagId;
|
||||||
|
if (subRole == eSubRol.Elite && s.eliteBagId > 0 && index < s.eliteFloorDrops) return s.eliteBagId;
|
||||||
|
return zoneBagId;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>폭발 드랍 1건. 백을 다시 추첨하고, 빈손이면 골드로 대체한다. 러너가 간격을 두고 부른다.</summary>
|
/// <summary>폭발 드랍 1건. 백을 다시 추첨하고, 빈손이면 골드로 대체한다. 러너가 간격을 두고 부른다.</summary>
|
||||||
internal static void SpawnBurstOne(int bagId, int baseGold, Vector3 position)
|
internal static void SpawnBurstOne(int bagId, int baseGold, Vector3 position)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -161,16 +161,16 @@ namespace WL.Combat.Loot
|
||||||
|
|
||||||
// ───────────────────────────────────────────────── 폭발 드랍
|
// ───────────────────────────────────────────────── 폭발 드랍
|
||||||
/// <summary>보스/엘리트 추가 드랍을 간격을 두고 낸다. ClusterBurst 슬롯을 잡았으면 마지막에 반납한다.</summary>
|
/// <summary>보스/엘리트 추가 드랍을 간격을 두고 낸다. ClusterBurst 슬롯을 잡았으면 마지막에 반납한다.</summary>
|
||||||
public void RunBurst(int count, int bagId, int baseGold, Vector3 position, float interval, bool heldCluster)
|
public void RunBurst(int count, int bagId, int baseGold, Vector3 position, float interval, bool heldCluster, eSubRol subRole)
|
||||||
{
|
{
|
||||||
StartCoroutine(CoBurst(count, bagId, baseGold, position, interval, heldCluster));
|
StartCoroutine(CoBurst(count, bagId, baseGold, position, interval, heldCluster, subRole));
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerator CoBurst(int count, int bagId, int baseGold, Vector3 position, float interval, bool heldCluster)
|
IEnumerator CoBurst(int count, int bagId, int baseGold, Vector3 position, float interval, bool heldCluster, eSubRol subRole)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
LootBurst.SpawnBurstOne(bagId, baseGold, position);
|
LootBurst.SpawnBurstOne(LootBurst.BagForBurst(WLLootSettings.Instance, subRole, i, bagId), baseGold, position);
|
||||||
if (i < count - 1 && interval > 0f) yield return new WaitForSeconds(interval);
|
if (i < count - 1 && interval > 0f) yield return new WaitForSeconds(interval);
|
||||||
}
|
}
|
||||||
if (heldCluster) EffectBudget.Release(EffectBudgetKind.ClusterBurst);
|
if (heldCluster) EffectBudget.Release(EffectBudgetKind.ClusterBurst);
|
||||||
|
|
|
||||||
|
|
@ -169,6 +169,17 @@ namespace WL.Combat.Loot
|
||||||
[Tooltip("EffectBudget.ClusterBurst 슬롯을 잡고 폭발 드랍을 시작한다. 811e 군집 폭발과 슬롯(상한 1)을 공유하므로 기본 off.")]
|
[Tooltip("EffectBudget.ClusterBurst 슬롯을 잡고 폭발 드랍을 시작한다. 811e 군집 폭발과 슬롯(상한 1)을 공유하므로 기본 off.")]
|
||||||
public bool burstUseClusterBudget = false;
|
public bool burstUseClusterBudget = false;
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────── ④-B 등급 하한 백 (기준서 §E 4-1 · #813f2)
|
||||||
|
[Header("④-B 등급 하한 백 (엘리트 ≥3 · 보스 ≥5)")]
|
||||||
|
[Tooltip("엘리트 폭발 드랍 앞 N 건이 쓰는 RandomBag ID(등급 3~5 확정 · 8130002). 0 = 존 백 그대로.")]
|
||||||
|
public int eliteBagId = 8130002;
|
||||||
|
|
||||||
|
[Tooltip("엘리트 폭발 드랍 중 eliteBagId 를 쓰는 건수(앞에서부터). 나머지는 몹의 존 백.")]
|
||||||
|
public int eliteFloorDrops = 1;
|
||||||
|
|
||||||
|
[Tooltip("보스 폭발 드랍이 쓰는 RandomBag ID(등급 5~7 확정 · 8130003 · 전부 beamMinGrade 4 이상). 0 = 존 백 그대로.")]
|
||||||
|
public int bossBagId = 8130003;
|
||||||
|
|
||||||
// ─────────────────────────────────────────── ⑤ 동시 드랍 오브젝트 상한
|
// ─────────────────────────────────────────── ⑤ 동시 드랍 오브젝트 상한
|
||||||
[Header("⑤ 동시 드랍 오브젝트 상한 (모바일 과밀 방지)")]
|
[Header("⑤ 동시 드랍 오브젝트 상한 (모바일 과밀 방지)")]
|
||||||
[Tooltip("살아 있는 드랍 오브젝트가 이 수를 넘으면 폭발 드랍의 추가 건을 더 내지 않는다(원본 기본 드랍은 절대 막지 않는다). " +
|
[Tooltip("살아 있는 드랍 오브젝트가 이 수를 넘으면 폭발 드랍의 추가 건을 더 내지 않는다(원본 기본 드랍은 절대 막지 않는다). " +
|
||||||
|
|
|
||||||
|
|
@ -56,5 +56,8 @@ MonoBehaviour:
|
||||||
burstRollsBag: 1
|
burstRollsBag: 1
|
||||||
burstGoldShare: 0.25
|
burstGoldShare: 0.25
|
||||||
burstUseClusterBudget: 0
|
burstUseClusterBudget: 0
|
||||||
|
eliteBagId: 8130002
|
||||||
|
eliteFloorDrops: 1
|
||||||
|
bossBagId: 8130003
|
||||||
maxConcurrentDrops: 24
|
maxConcurrentDrops: 24
|
||||||
beamSuppressAboveDrops: 12
|
beamSuppressAboveDrops: 12
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue