using System.Collections.Generic; using UnityEngine; namespace TKS.OutGame { /// 뽑기 1장 결과. public class GachaPull { public string heroId; /// 뽑힌 희귀도 등급(1~3 · 확률 표 기준). public int tier; public bool isNew; public bool pickup; public bool pity; public int shards; public int gold; } /// /// 영웅·유닛 뽑기(기획서 §4 · gacha.csv · gacha_pool.csv): 등급 확률 p_r3/p_r2/p_r1 · 천장 = pity 번째 ★★★ 확정(획득 시 0) · /// ★★★ 중 pickup_share 는 픽업 영웅 · 10연 = ten_guarantee_rarity 이상 1 보장 · 병사 유닛 = ★ 풀 · 중복 = 조각(shard_dup_r*) / 병사 = 골드(soldier_dup_gold) · 첫 10연 무료. /// public static class GachaRules { public static bool FirstTenFree(OutGameData db, SaveData d) { return db.Gacha.FreeFirstTen && !d.gacha.firstTenUsed; } /// count 회 비용(1 = cost_single · ten_count = cost_ten · 첫 10연 무료). public static int Cost(OutGameData db, SaveData d, int count) { var g = db.Gacha; if (count == g.TenCount) return FirstTenFree(db, d) ? 0 : g.CostTen; return g.CostSingle * count; } public static string CanDraw(OutGameData db, SaveData d, int count) { int cost = Cost(db, d, count); if (d.gem < cost) return "보옥 부족 " + d.gem + " / " + cost; if (Candidates(db, 1).Count == 0) return "뽑기 풀 비었음"; return null; } /// 풀에서 등급 tier 에 나오는 유닛(영웅 = 희귀도 = tier · 병사 = ★ 풀). public static List Candidates(OutGameData db, int tier) { var res = new List(); foreach (var p in db.Gacha.pool) { if (!p.inPool) continue; var h = db.Heroes.Get(p.heroId); if (h == null) continue; int t = h.isSoldier ? 1 : h.rarity; if (t == tier) res.Add(p); } return res; } public static List Draw(OutGameData db, SaveData d, int count, System.Random rng) { var res = new List(); if (CanDraw(db, d, count) != null) return res; var g = db.Gacha; if (count == g.TenCount && FirstTenFree(db, d)) d.gacha.firstTenUsed = true; else d.gem -= Cost(db, d, count); int gearSlots = HeroRules.GearSlotCount(db); bool guaranteed = false; for (int i = 0; i < count; i++) { int n = d.gacha.pity + 1; int tier; bool pity = n >= g.Pity; if (pity) tier = 3; else { double r = rng.NextDouble(); tier = r < g.P(3) ? 3 : r < g.P(3) + g.P(2) ? 2 : 1; } if (tier >= g.TenGuaranteeRarity) guaranteed = true; if (count == g.TenCount && i == count - 1 && !guaranteed) tier = g.TenGuaranteeRarity; d.gacha.pity = tier == 3 ? 0 : n; d.gacha.total++; var pull = Pick(db, tier, rng); if (pull == null) continue; pull.pity = pity; var h = db.Heroes.Get(pull.heroId); if (HeroRules.Grant(db, d, h, gearSlots)) pull.isNew = true; else if (h.isSoldier) { pull.gold = db.Economy.SoldierDupGold; d.gold += pull.gold; } else { pull.shards = db.Economy.ShardDup(h.rarity); d.Hero(h.id).shards += pull.shards; } res.Add(pull); } return res; } static GachaPull Pick(OutGameData db, int tier, System.Random rng) { var list = Candidates(db, tier); for (int t = tier; list.Count == 0 && t > 1; t--) list = Candidates(db, t - 1); // 그 등급 풀이 비면 아래 등급 if (list.Count == 0) return null; if (tier == 3) { var pick = new List(); var rest = new List(); foreach (var p in list) (p.pickup ? pick : rest).Add(p); if (pick.Count > 0 && (rest.Count == 0 || rng.NextDouble() < db.Gacha.PickupShare)) list = pick; else if (rest.Count > 0) list = rest; } var e = list[rng.Next(list.Count)]; return new GachaPull { heroId = e.heroId, tier = tier, pickup = e.pickup && tier == 3 }; } } }