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
+3
View File
@@ -24,6 +24,7 @@ import (
"honoka-chan/internal/handler/api/profile"
"honoka-chan/internal/handler/api/reward"
"honoka-chan/internal/handler/api/scenario"
"honoka-chan/internal/handler/api/secretbox"
"honoka-chan/internal/handler/api/stamp"
"honoka-chan/internal/handler/api/subscenario"
"honoka-chan/internal/handler/api/unit"
@@ -96,6 +97,8 @@ func api(ctx *gin.Context) {
result, err = reward.RewardApi(v.Action)
case "scenario":
result, err = scenario.ScenarioApi(ctx, v.Action)
case "secretbox":
result, err = secretbox.SecretBoxApi(v.Action)
case "stamp":
result, err = stamp.StampApi(v.Action)
case "subscenario":
+32 -36
View File
@@ -11,48 +11,44 @@ func bannerList() (res any, err error) {
TimeLimit: "2037-12-31 23:59:59",
BannerList: []bannerapischema.BannerList{
{
BannerType: 1,
TargetID: 1743,
AssetPath: "assets/image/secretbox/icon/s_ba_1718_1.png",
FixedFlag: false,
BackSide: false,
BannerID: 101151,
StartDate: "2013-04-15 00:00:00",
EndDate: "2037-12-31 23:59:59",
AddUnitStartDate: "2022-01-01 00:00:00",
BannerType: 1,
TargetID: 1718,
AssetPath: "assets/image/secretbox/icon/s_ba_1718_1.png",
FixedFlag: false,
BackSide: false,
BannerID: 101151,
StartDate: "2013-04-15 00:00:00",
EndDate: "2037-12-31 23:59:59",
},
{
BannerType: 1,
TargetID: 1741,
AssetPath: "assets/image/secretbox/icon/s_ba_1719_1.png",
FixedFlag: false,
BackSide: false,
BannerID: 101150,
StartDate: "2013-04-15 00:00:00",
EndDate: "2037-12-31 23:59:59",
AddUnitStartDate: "2022-01-01 00:00:00",
BannerType: 1,
TargetID: 1719,
AssetPath: "assets/image/secretbox/icon/s_ba_1719_1.png",
FixedFlag: false,
BackSide: false,
BannerID: 101150,
StartDate: "2013-04-15 00:00:00",
EndDate: "2037-12-31 23:59:59",
},
{
BannerType: 1,
TargetID: 1740,
AssetPath: "assets/image/secretbox/icon/s_ba_1720_1.png",
FixedFlag: false,
BackSide: false,
BannerID: 101149,
StartDate: "2013-04-15 00:00:00",
EndDate: "2037-12-31 23:59:59",
AddUnitStartDate: "2022-01-01 00:00:00",
BannerType: 1,
TargetID: 1720,
AssetPath: "assets/image/secretbox/icon/s_ba_1720_1.png",
FixedFlag: false,
BackSide: false,
BannerID: 101149,
StartDate: "2013-04-15 00:00:00",
EndDate: "2037-12-31 23:59:59",
},
{
BannerType: 1,
TargetID: 1739,
AssetPath: "assets/image/secretbox/icon/s_ba_1721_1.png",
FixedFlag: false,
BackSide: false,
BannerID: 101144,
StartDate: "2013-04-15 00:00:00",
EndDate: "2037-12-31 23:59:59",
AddUnitStartDate: "2022-01-01 00:00:00",
BannerType: 1,
TargetID: 1721,
AssetPath: "assets/image/secretbox/icon/s_ba_1721_1.png",
FixedFlag: false,
BackSide: false,
BannerID: 101144,
StartDate: "2013-04-15 00:00:00",
EndDate: "2037-12-31 23:59:59",
},
{
BannerType: 2,
+44
View File
@@ -0,0 +1,44 @@
package secretbox
import (
secretboxapischema "honoka-chan/internal/schema/api/secretbox"
honokautils "honoka-chan/internal/utils"
"time"
)
func all() (res any, err error) {
secretBoxData, err := honokautils.LoadServerData[secretboxapischema.AllData]("secretbox_data.json")
if err != nil {
return nil, err
}
secretBoxData.UseCache = 0
secretBoxData.IsUnitMax = false
secretBoxData.GaugeInfo.MaxGaugePoint = 100
secretBoxData.GaugeInfo.GaugePoint = 0
for i := range secretBoxData.ItemList {
if secretBoxData.ItemList[i].Amount < 999 {
secretBoxData.ItemList[i].Amount = 999
}
}
for i := range secretBoxData.MemberCategoryList {
for j := range secretBoxData.MemberCategoryList[i].PageList {
for k := range secretBoxData.MemberCategoryList[i].PageList[j].ButtonList {
for m := range secretBoxData.MemberCategoryList[i].PageList[j].ButtonList[k].CostList {
secretBoxData.MemberCategoryList[i].PageList[j].ButtonList[k].CostList[m].Payable = true
}
}
}
}
res = secretboxapischema.AllResp{
Result: secretBoxData,
Status: 200,
CommandNum: false,
TimeStamp: time.Now().Unix(),
}
return res, nil
}
@@ -0,0 +1,15 @@
package secretbox
import (
"fmt"
)
func SecretBoxApi(action string) (res any, err error) {
switch action {
case "all":
res, err = all()
default:
err = fmt.Errorf("unimplemented action: secretbox: %s", action)
}
return res, err
}