103 lines
4.3 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|