Implement secretbox handlers and align gacha behavior

- Add secretbox API schemas, handlers, and routes for all/showDetail/pon/multi
- Load secretbox server data through the shared serverdata loader
- Fix rarity mapping and pool rules for SR/SSR/UR special boxes
- Implement thanks festival pool behavior, including UR guarantee and SSR/UR-only rates
- Correct animation-related fields such as is_hit and lowest_rarity, and add rare UR promotion animation

Signed-off-by: Sean Du <do4suki@gmail.com>
This commit is contained in:
2026-06-05 21:57:04 +08:00
parent acd9be627e
commit 750c2cf3d0
20 changed files with 2558 additions and 576 deletions
+6 -4
View File
@@ -1,10 +1,12 @@
package apischema
type ApiReq struct {
Module string `json:"module"`
UserID int `json:"user_id"`
Action string `json:"action"`
Timestamp int64 `json:"timeStamp"`
Module string `json:"module"`
UserID int `json:"user_id"`
Action string `json:"action"`
ID int `json:"id"`
SecretBoxID int `json:"secret_box_id"`
Timestamp int64 `json:"timeStamp"`
}
type ApiResp struct {
+9 -10
View File
@@ -1,16 +1,15 @@
package bannerapischema
type BannerList struct {
BannerType int `json:"banner_type"`
TargetID int `json:"target_id"`
AssetPath string `json:"asset_path"`
FixedFlag bool `json:"fixed_flag"`
BackSide bool `json:"back_side"`
BannerID int `json:"banner_id"`
StartDate string `json:"start_date"`
EndDate string `json:"end_date"`
AddUnitStartDate string `json:"add_unit_start_date,omitempty"`
WebviewURL string `json:"webview_url,omitempty"`
BannerType int `json:"banner_type"`
TargetID int `json:"target_id"`
AssetPath string `json:"asset_path"`
FixedFlag bool `json:"fixed_flag"`
BackSide bool `json:"back_side"`
BannerID int `json:"banner_id"`
StartDate string `json:"start_date"`
EndDate string `json:"end_date"`
WebviewURL string `json:"webview_url,omitempty"`
}
type ListData struct {
+80
View File
@@ -0,0 +1,80 @@
package secretboxapischema
type ItemAmount struct {
ItemID int `json:"item_id"`
Amount int `json:"amount"`
}
type GaugeInfo struct {
MaxGaugePoint int `json:"max_gauge_point"`
GaugePoint int `json:"gauge_point"`
}
type SecretBoxAnimationAssets struct {
Type int `json:"type"`
BackgroundAsset string `json:"background_asset"`
AdditionalAsset1 string `json:"additional_asset_1"`
AdditionalAsset2 string `json:"additional_asset_2"`
AdditionalAsset3 string `json:"additional_asset_3"`
}
type SecretBoxCost struct {
ID int `json:"id"`
Type int `json:"type"`
Amount int `json:"amount"`
ItemID *int `json:"item_id"`
UnitCount int `json:"unit_count"`
Payable bool `json:"payable"`
}
type SecretBoxButton struct {
SecretBoxButtonType int `json:"secret_box_button_type"`
CostList []SecretBoxCost `json:"cost_list"`
SecretBoxName string `json:"secret_box_name"`
LimitCount int `json:"limit_count,omitempty"`
LimitUnderMessage string `json:"limit_under_message,omitempty"`
LimitUnderMessageSuffix string `json:"limit_under_message_suffix,omitempty"`
BalloonAsset string `json:"balloon_asset,omitempty"`
}
type SecretBoxInfo struct {
SecretBoxID int `json:"secret_box_id"`
SecretBoxType int `json:"secret_box_type"`
Name string `json:"name"`
Description string `json:"description"`
StartDate string `json:"start_date"`
EndDate string `json:"end_date"`
ShowEndDate string `json:"show_end_date"`
AddGauge int `json:"add_gauge"`
AlwaysDisplayFlag int `json:"always_display_flag"`
PonCount int `json:"pon_count"`
PonUpperLimit int `json:"pon_upper_limit"`
}
type SecretBoxPage struct {
MenuAsset string `json:"menu_asset"`
PageOrder int `json:"page_order"`
AnimationAssets SecretBoxAnimationAssets `json:"animation_assets"`
ButtonList []SecretBoxButton `json:"button_list"`
SecretBoxInfo SecretBoxInfo `json:"secret_box_info"`
}
type MemberCategoryPageList struct {
MemberCategory int `json:"member_category"`
PageList []SecretBoxPage `json:"page_list"`
}
type AllData struct {
UseCache int `json:"use_cache"`
IsUnitMax bool `json:"is_unit_max"`
ItemList []ItemAmount `json:"item_list"`
GaugeInfo GaugeInfo `json:"gauge_info"`
MemberCategoryList []MemberCategoryPageList `json:"member_category_list"`
}
type AllResp struct {
Result AllData `json:"result"`
Status int `json:"status"`
CommandNum bool `json:"commandNum"`
TimeStamp int64 `json:"timeStamp"`
}
+19
View File
@@ -0,0 +1,19 @@
package secretboxschema
type MultiReq struct {
Module string `json:"module"`
Action string `json:"action"`
Mgd int `json:"mgd"`
ID int `json:"id"`
SecretBoxID int `json:"secret_box_id"`
Count int `json:"count"`
TimeStamp int64 `json:"timeStamp"`
CommandNum string `json:"commandNum"`
UnitTypeIds []any `json:"unit_type_ids"`
}
type MultiResp struct {
ResponseData PonData `json:"response_data"`
ReleaseInfo []any `json:"release_info"`
StatusCode int `json:"status_code"`
}
+96
View File
@@ -0,0 +1,96 @@
package secretboxschema
import secretboxapischema "honoka-chan/internal/schema/api/secretbox"
type PonReq struct {
Module string `json:"module"`
Mgd int `json:"mgd"`
Action string `json:"action"`
TimeStamp int64 `json:"timeStamp"`
ID int `json:"id"`
CommandNum string `json:"commandNum"`
SecretBoxID int `json:"secret_box_id"`
UnitTypeIds []any `json:"unit_type_ids"`
}
type SecretBoxUnitItem struct {
UnitOwningUserID int `json:"unit_owning_user_id"`
UnitOwningIDs []int `json:"unit_owning_ids"`
UnitID int `json:"unit_id"`
Exp int `json:"exp"`
NextExp int `json:"next_exp"`
Level int `json:"level"`
MaxLevel int `json:"max_level"`
LevelLimitID int `json:"level_limit_id"`
Rank int `json:"rank"`
MaxRank int `json:"max_rank"`
Love int `json:"love"`
MaxLove int `json:"max_love"`
UnitSkillLevel int `json:"unit_skill_level"`
SkillLevel int `json:"skill_level"`
UnitSkillExp int `json:"unit_skill_exp"`
MaxHp int `json:"max_hp"`
FavoriteFlag bool `json:"favorite_flag"`
DisplayRank int `json:"display_rank"`
UnitRemovableSkillCapacity int `json:"unit_removable_skill_capacity"`
MaxRemovableSkillCapacity int `json:"max_removable_skill_capacity"`
Attribute int `json:"attribute"`
Smile int `json:"smile"`
Cute int `json:"cute"`
Cool int `json:"cool"`
IsRankMax bool `json:"is_rank_max"`
IsLoveMax bool `json:"is_love_max"`
IsLevelMax bool `json:"is_level_max"`
IsSigned bool `json:"is_signed"`
IsSkillLevelMax bool `json:"is_skill_level_max"`
IsRemovableSkillCapacityMax bool `json:"is_removable_skill_capacity_max"`
IsSupportMember bool `json:"is_support_member"`
NewUnitFlag bool `json:"new_unit_flag"`
UnitRarityID int `json:"unit_rarity_id"`
IsHit bool `json:"is_hit"`
RemovableSkillIDs []int `json:"removable_skill_ids"`
InsertDate string `json:"insert_date"`
TotalSmile int `json:"total_smile"`
TotalCute int `json:"total_cute"`
TotalCool int `json:"total_cool"`
TotalHp int `json:"total_hp"`
}
type SecretBoxRewardItem struct {
ItemID int `json:"item_id"`
AddType int `json:"add_type"`
Amount int `json:"amount"`
ItemCategoryID int `json:"item_category_id"`
RewardBoxFlag bool `json:"reward_box_flag"`
InsertDate string `json:"insert_date"`
}
type SecretBoxItems struct {
Unit []SecretBoxUnitItem `json:"unit"`
Item []SecretBoxRewardItem `json:"item"`
}
type PonData struct {
IsUnitMax bool `json:"is_unit_max"`
ItemList []secretboxapischema.ItemAmount `json:"item_list"`
GaugeInfo secretboxapischema.GaugeInfo `json:"gauge_info"`
ButtonList []secretboxapischema.SecretBoxButton `json:"button_list"`
SecretBoxInfo secretboxapischema.SecretBoxInfo `json:"secret_box_info"`
SecretBoxItems SecretBoxItems `json:"secret_box_items"`
BeforeUserInfo any `json:"before_user_info"`
AfterUserInfo any `json:"after_user_info"`
FreeMuseGachaFlag bool `json:"free_muse_gacha_flag"`
FreeAqoursGachaFlag bool `json:"free_aqours_gacha_flag"`
LowestRarity int `json:"lowest_rarity"`
PromotionPerformanceRate int `json:"promotion_performance_rate"`
SecretBoxParcelType int `json:"secret_box_parcel_type"`
LimitBonusInfo []any `json:"limit_bonus_info"`
LimitBonusRewards []any `json:"limit_bonus_rewards"`
UnitSupportList []any `json:"unit_support_list"`
}
type PonResp struct {
ResponseData PonData `json:"response_data"`
ReleaseInfo []any `json:"release_info"`
StatusCode int `json:"status_code"`
}
+33
View File
@@ -0,0 +1,33 @@
package secretboxschema
type ShowDetailReq struct {
Module string `json:"module"`
Action string `json:"action"`
TimeStamp int64 `json:"timeStamp"`
Mgd int `json:"mgd"`
SecretBoxID int `json:"secret_box_id"`
CommandNum string `json:"commandNum"`
}
type UnitLineUp struct {
Rarity int `json:"rarity"`
UnitIDs []int `json:"unit_ids"`
}
type ButtonTypeUnitLineUp struct {
SecretBoxButtonType int `json:"secret_box_button_type"`
SecretBoxName string `json:"secret_box_name"`
UnitLineUp []UnitLineUp `json:"unit_line_up"`
}
type ShowDetailData struct {
ButtonTypeUnitLineUp []ButtonTypeUnitLineUp `json:"button_type_unit_line_up,omitempty"`
UnitLineUp []UnitLineUp `json:"unit_line_up,omitempty"`
URL string `json:"url"`
}
type ShowDetailResp struct {
ResponseData ShowDetailData `json:"response_data"`
ReleaseInfo []any `json:"release_info"`
StatusCode int `json:"status_code"`
}