Pack opening

This commit is contained in:
gamer147
2026-05-24 02:03:13 -04:00
parent bdff142d16
commit 79209bd70b
41 changed files with 37320 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
using MessagePack;
using System.Text.Json.Serialization;
namespace SVSim.EmulatedEntrypoint.Models.Dtos;
[MessagePackObject]
public class PackBannerDto
{
[JsonPropertyName("banner_name")]
[Key("banner_name")]
public string BannerName { get; set; } = string.Empty;
[JsonPropertyName("dialog_title")]
[Key("dialog_title")]
public string DialogTitle { get; set; } = string.Empty;
}

View File

@@ -0,0 +1,43 @@
using MessagePack;
using System.Text.Json.Serialization;
namespace SVSim.EmulatedEntrypoint.Models.Dtos;
[MessagePackObject]
public class PackChildGachaDto
{
[JsonPropertyName("gacha_id")]
[Key("gacha_id")]
public int GachaId { get; set; }
[JsonPropertyName("type_detail")]
[Key("type_detail")]
public int TypeDetail { get; set; }
[JsonPropertyName("cost")]
[Key("cost")]
public int Cost { get; set; }
[JsonPropertyName("count")]
[Key("count")]
public int Count { get; set; } = 8;
/// <summary>Stringified on the wire when present (prod sends "10001" not 10001).</summary>
[JsonPropertyName("item_id")]
[Key("item_id")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? ItemId { get; set; }
[JsonPropertyName("item_number")]
[Key("item_number")]
public int ItemNumber { get; set; }
[JsonPropertyName("is_daily_single")]
[Key("is_daily_single")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public bool IsDailySingle { get; set; }
[JsonPropertyName("override_increase_gacha_point")]
[Key("override_increase_gacha_point")]
public string OverrideIncreaseGachaPoint { get; set; } = "0";
}

View File

@@ -0,0 +1,110 @@
using MessagePack;
using System.Text.Json.Serialization;
namespace SVSim.EmulatedEntrypoint.Models.Dtos;
[MessagePackObject]
public class PackConfigDto
{
[JsonPropertyName("parent_gacha_id")]
[Key("parent_gacha_id")]
public int ParentGachaId { get; set; }
[JsonPropertyName("base_pack_id")]
[Key("base_pack_id")]
public int BasePackId { get; set; }
[JsonPropertyName("override_draw_effect_pack_id")]
[Key("override_draw_effect_pack_id")]
public int OverrideDrawEffectPackId { get; set; }
[JsonPropertyName("override_ui_effect_pack_id")]
[Key("override_ui_effect_pack_id")]
public int OverrideUiEffectPackId { get; set; }
[JsonPropertyName("gacha_type")]
[Key("gacha_type")]
public int GachaType { get; set; }
[JsonPropertyName("sleeve_id")]
[Key("sleeve_id")]
public int SleeveId { get; set; } = 3000011;
[JsonPropertyName("special_sleeve_id")]
[Key("special_sleeve_id")]
public int SpecialSleeveId { get; set; }
[JsonPropertyName("commence_date")]
[Key("commence_date")]
public string CommenceDate { get; set; } = string.Empty;
[JsonPropertyName("complete_date")]
[Key("complete_date")]
public string CompleteDate { get; set; } = string.Empty;
[JsonPropertyName("cardpack_banner_list")]
[Key("cardpack_banner_list")]
public List<PackBannerDto> CardpackBannerList { get; set; } = new();
[JsonPropertyName("gacha_detail")]
[Key("gacha_detail")]
public string GachaDetail { get; set; } = string.Empty;
[JsonPropertyName("child_gacha_info")]
[Key("child_gacha_info")]
public List<PackChildGachaDto> ChildGachaInfo { get; set; } = new();
[JsonPropertyName("open_count")]
[Key("open_count")]
public int OpenCount { get; set; }
[JsonPropertyName("open_count_limit")]
[Key("open_count_limit")]
public int OpenCountLimit { get; set; }
[JsonPropertyName("is_hide")]
[Key("is_hide")]
public int IsHide { get; set; }
[JsonPropertyName("pack_category")]
[Key("pack_category")]
public int PackCategory { get; set; }
/// <summary>
/// Null when the pack has no gacha-point participation. The key MUST be present on the wire
/// (explicit null) — client at PackInfoTask.cs:126 does <c>if (jsonData2["gacha_point"] != null)</c>,
/// a direct LitJson key access that throws KeyNotFoundException when the key is absent
/// (only protects against null *value*, not missing *key*). Override the global
/// WhenWritingNull per [[project_wire_null_policy]] memory.
/// </summary>
[JsonPropertyName("gacha_point")]
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
[Key("gacha_point")]
public PackGachaPointDto? GachaPoint { get; set; }
[JsonPropertyName("is_pre_release")]
[Key("is_pre_release")]
public bool IsPreRelease { get; set; }
[JsonPropertyName("exists_purchase_reward")]
[Key("exists_purchase_reward")]
public bool ExistsPurchaseReward { get; set; }
[JsonPropertyName("is_new")]
[Key("is_new")]
public bool IsNew { get; set; }
/// <summary>
/// Prod sends an object <c>{"sales_period_time":"..."}</c> when set and an array <c>[]</c>
/// when unset. v1 always emits an empty object when the field is null on the entity —
/// matches the active-window case and the client tolerates both shapes via
/// <c>ShopExpirtyInfo</c>'s LitJson parser. Revisit if a capture proves otherwise.
/// </summary>
[JsonPropertyName("sales_period_info")]
[Key("sales_period_info")]
public Dictionary<string, string?> SalesPeriodInfo { get; set; } = new();
[JsonPropertyName("poster_type")]
[Key("poster_type")]
public int PosterType { get; set; }
}

View File

@@ -0,0 +1,32 @@
using MessagePack;
using System.Text.Json.Serialization;
namespace SVSim.EmulatedEntrypoint.Models.Dtos;
/// <summary>
/// gacha_point block inside /pack/info entries. Prod ships strings for pack_id/increase_gacha_point;
/// mirror exactly per project_wire_key_serialization.
/// </summary>
[MessagePackObject]
public class PackGachaPointDto
{
[JsonPropertyName("pack_id")]
[Key("pack_id")]
public string PackId { get; set; } = "0";
[JsonPropertyName("gacha_point")]
[Key("gacha_point")]
public int GachaPoint { get; set; }
[JsonPropertyName("increase_gacha_point")]
[Key("increase_gacha_point")]
public string IncreaseGachaPoint { get; set; } = "0";
[JsonPropertyName("exchangeable_gacha_point")]
[Key("exchangeable_gacha_point")]
public int ExchangeableGachaPoint { get; set; }
[JsonPropertyName("is_exchangeable_gacha_point")]
[Key("is_exchangeable_gacha_point")]
public bool IsExchangeableGachaPoint { get; set; }
}

View File

@@ -0,0 +1,43 @@
using System.Text.Json.Serialization;
using MessagePack;
using SVSim.EmulatedEntrypoint.Models.Dtos.Requests;
namespace SVSim.EmulatedEntrypoint.Models.Dtos.Requests.Pack;
/// <summary>
/// Inbound /pack/open body. Accepts ALL three client-side overloads in one DTO — fields
/// for Starter (<c>class_id</c>) and Skin (<c>target_card_id</c>) are nullable so we can
/// reject those overloads in the controller without a custom binder.
/// See <c>Wizard/PackOpenTask.cs</c> for the three SetParameter variants.
/// </summary>
[MessagePackObject]
public class PackOpenRequest : BaseRequest
{
[JsonPropertyName("parent_gacha_id")]
[Key("parent_gacha_id")]
public int ParentGachaId { get; set; }
[JsonPropertyName("gacha_id")]
[Key("gacha_id")]
public int GachaId { get; set; }
[JsonPropertyName("gacha_type")]
[Key("gacha_type")]
public int GachaType { get; set; }
[JsonPropertyName("pack_number")]
[Key("pack_number")]
public int PackNumber { get; set; }
[JsonPropertyName("exclude_card_ids")]
[Key("exclude_card_ids")]
public long[] ExcludeCardIds { get; set; } = Array.Empty<long>();
[JsonPropertyName("class_id")]
[Key("class_id")]
public int? ClassId { get; set; }
[JsonPropertyName("target_card_id")]
[Key("target_card_id")]
public long? TargetCardId { get; set; }
}

View File

@@ -0,0 +1,12 @@
using MessagePack;
using System.Text.Json.Serialization;
namespace SVSim.EmulatedEntrypoint.Models.Dtos.Responses.Pack;
[MessagePackObject]
public class PackInfoResponse
{
[JsonPropertyName("pack_config_list")]
[Key("pack_config_list")]
public List<PackConfigDto> PackConfigList { get; set; } = new();
}

View File

@@ -0,0 +1,46 @@
using MessagePack;
using System.Text.Json.Serialization;
namespace SVSim.EmulatedEntrypoint.Models.Dtos.Responses.Pack;
[MessagePackObject]
public class PackOpenResponse
{
[JsonPropertyName("pack_list")]
[Key("pack_list")]
public List<CardPackEntryDto> PackList { get; set; } = new();
[JsonPropertyName("reward_list")]
[Key("reward_list")]
public List<RewardListEntry> RewardList { get; set; } = new();
[JsonPropertyName("rewards")]
[Key("rewards")]
public List<object> Rewards { get; set; } = new();
[JsonPropertyName("is_special_effect")]
[Key("is_special_effect")]
public bool IsSpecialEffect { get; set; }
/// <summary>Empty array literal — matches prod when no missions completed.</summary>
[JsonPropertyName("mission_result")]
[Key("mission_result")]
public List<object> MissionResult { get; set; } = new();
}
[MessagePackObject]
public class CardPackEntryDto
{
[JsonPropertyName("card_id")]
[Key("card_id")]
public long CardId { get; set; }
[JsonPropertyName("rarity")]
[Key("rarity")]
public int Rarity { get; set; }
/// <summary>Always 1 per drawn slot — matches prod sample shape.</summary>
[JsonPropertyName("number")]
[Key("number")]
public int Number { get; set; } = 1;
}

View File

@@ -0,0 +1,31 @@
using MessagePack;
using System.Text.Json.Serialization;
namespace SVSim.EmulatedEntrypoint.Models.Dtos;
/// <summary>
/// One entry of <c>reward_list</c> on /pack/open (and other grant-emitting endpoints).
/// Client at <c>PlayerStaticData.UpdateHaveUserGoodsNumByJsonData</c> reads these and writes
/// <c>UserRupyCount = reward_num</c>, <c>UserCrystalCount = reward_num</c>, etc. —
/// <b>reward_num is the NEW POST-STATE TOTAL, not a delta</b>. Without these entries the
/// client's cached currency/collection counts stay stale until a full refresh (mypage, restart).
///
/// reward_type values are <c>Wizard.UserGoods.Type</c>: 1=RedEther, 2=Crystal, 4=Item, 5=Card,
/// 6=Sleeve, 7=Emblem, 8=Degree, 9=Rupy, 10=Skin, 11=SpotCard, 12=SpotCardPoint, etc.
/// reward_id is 0 for non-instanced goods (Rupy, Crystal, RedEther) and the entity id for cards.
/// </summary>
[MessagePackObject]
public class RewardListEntry
{
[JsonPropertyName("reward_type")]
[Key("reward_type")]
public int RewardType { get; set; }
[JsonPropertyName("reward_id")]
[Key("reward_id")]
public long RewardId { get; set; }
[JsonPropertyName("reward_num")]
[Key("reward_num")]
public int RewardNum { get; set; }
}