VSClone/Assets/Scripts/Sim/Game.Closet.cs

103 lines
4.3 KiB
C#

using System;
using System.Numerics;
// 의상: 공통 옷장 레벨(Clothes 72단계 · StatRate = 전역 배율 · 장착 의상과 무관) + 의상 32종 해금·장착 — systems.md §5
namespace VS.Sim
{
public static class ClosetUnlock { public const int Default = 0, Free = 1, Ticket = 2, InEvent = 4; }
public partial class Game
{
public ClothesRow ClosetLevelRow(int level) { int i = T.ClothesRowIndex(level); return i >= 0 ? T.Clothes.Levels[i] : null; }
public bool ClosetMaxed => S.closetLevel >= ClosetMaxLevel;
public ClothesRow ClosetNext => ClosetMaxed ? null : ClosetLevelRow(S.closetLevel + 1);
/// GetUpgradeQuote RVA 0x23EAE14 — 할인가 · 첫 구매는 10% (FirstPurchasePricePercent)
public BigInteger ClosetNextPrice()
{
var n = ClosetNext;
if (n == null) return BigInteger.Zero;
var p = DiscountedPrice(T.ClothesPrice[T.ClothesRowIndex(n.Level)]);
if (!S.isBuyCloset && S.closetLevel < 1) p = p * T.X.closetFirstPurchasePercent / 100;
return p;
}
/// IsRankLocked RVA 0x23EAD74
public bool ClosetRankOk() { var n = ClosetNext; return n != null && n.RankDependency <= S.rankIndex; }
/// TryUpgrade RVA 0x23EB404 (1단계)
public BuyResult UpgradeCloset()
{
var n = ClosetNext;
if (n == null) return BuyResult.Max;
if (ClosetUpgrading) return BuyResult.Busy;
if (!ClosetRankOk()) return BuyResult.Locked;
var price = ClosetNextPrice();
if (price > Coin) return BuyResult.NoMoney;
SpendCoin(price);
int before = S.closetLevel;
S.closetLevel = n.Level;
if (n.UpgradeTime > 0f) S.closetUpgradeEnd = Now().AddSeconds(n.UpgradeTime).Ticks;
S.isBuyCloset = true;
MarkNewFreeOutfits(before, S.closetLevel);
OnClosetUpgraded();
return BuyResult.Ok;
}
public void SkipClosetUpgrade() { S.closetUpgradeEnd = 0; }
// ---- 의상(outfit) — IsUnlocked RVA 0x23ECD0C
public OutfitRow Outfit(int type) => T.Cfg.closet.outfits.Find(o => o.Type == type);
public bool OutfitUnlocked(int type)
{
if (type == 1000) return true;
var o = Outfit(type);
if (o == null) return false;
switch (o.UnlockType)
{
case ClosetUnlock.Default: return true;
case ClosetUnlock.Free: return S.closetLevel >= o.UnlockLevel;
default: return S.closetUnlocked.Contains(type);
}
}
/// GetOutfitTicketPrice RVA 0x23ED174 (광고 차감 = 클론에 없음)
public int OutfitTicketPrice(OutfitRow o) => Math.Max(o.TicketPrice, 1);
/// IsTicketOutfitRankLocked RVA 0x23ED230
public bool OutfitRankOk(OutfitRow o) => o.VisibilityRank <= 0 || S.rankIndex >= o.VisibilityRank;
void MarkNewFreeOutfits(int before, int after)
{
foreach (var o in T.Cfg.closet.outfits)
if (o.UnlockType == ClosetUnlock.Free && o.UnlockLevel > before && o.UnlockLevel <= after && !S.closetNew.Contains(o.Type)) S.closetNew.Add(o.Type);
}
/// TryStageTicketPurchase RVA 0x23ED45C
public BuyResult UnlockTicketOutfit(int type)
{
var o = Outfit(type);
if (o == null || o.UnlockType != ClosetUnlock.Ticket) return BuyResult.Locked;
if (OutfitUnlocked(type)) return BuyResult.Max;
if (!OutfitRankOk(o)) return BuyResult.Locked;
int price = OutfitTicketPrice(o);
if (S.ticket < price) return BuyResult.NoMoney;
S.ticket -= price;
S.closetUnlocked.Add(type);
if (!S.closetNew.Contains(type)) S.closetNew.Add(type);
return BuyResult.Ok;
}
/// 일일 보상 의상 (TryGrantEventOutfit)
public void GrantEventOutfit(int type) { if (!S.closetUnlocked.Contains(type)) { S.closetUnlocked.Add(type); S.closetNew.Add(type); } }
public bool EquipOutfit(int type)
{
if (!OutfitUnlocked(type)) return false;
S.closetCurrent = type;
S.closetNew.Remove(type);
return true;
}
}
}