package gameserver import ( "encoding/json" "fmt" "os" "sort" "sync" goproto "gac-hkrpg-go/protocol/proto" ) // ChallengeDataConfig mirrors challenge_data.json (Rust: ChallengeConfigData). type ChallengeDataConfig struct { StagesList []uint32 `json:"stages_list"` GroupsList []uint32 `json:"groups_list"` Challenges map[uint32]ChallengeStageData `json:"challenges"` Tierce map[uint32]ChallengeTierceStageData `json:"tierce"` Stages map[uint32]StageBattleData `json:"stages"` Peak map[string]PeakStageData `json:"peak"` } type ChallengeStageData struct { GroupID uint32 `json:"group_id"` Entrance uint32 `json:"entrance"` Entrance2 uint32 `json:"entrance2"` Group1 uint32 `json:"group1"` Group2 uint32 `json:"group2"` Monster1 uint32 `json:"monster1"` Monster2 uint32 `json:"monster2"` Event1 uint32 `json:"event1"` Event2 uint32 `json:"event2"` Buff uint32 `json:"buff"` } type ChallengeTierceStageData struct { Entrance uint32 `json:"entrance"` Group uint32 `json:"group"` Monster uint32 `json:"monster"` Event uint32 `json:"event"` Targets []uint32 `json:"targets"` Score uint32 `json:"score"` } type StageBattleData struct { Level uint32 `json:"level"` Monsters [][]uint32 `json:"monsters"` } type PeakStageData struct { EventID uint32 `json:"event_id"` HardEventID uint32 `json:"hard_event_id"` MonsterID uint32 `json:"monster_id"` DefaultBuff uint32 `json:"default_buff"` IsBoss bool `json:"is_boss"` Targets []uint32 `json:"targets"` HardTarget uint32 `json:"hard_target"` } var ( challengeOnce sync.Once challengeData ChallengeDataConfig ) func getChallengeData() *ChallengeDataConfig { challengeOnce.Do(func() { raw, err := os.ReadFile("challenge_data.json") if err != nil { Log("challenge_data.json not found!") return } if err := json.Unmarshal(raw, &challengeData); err != nil { Log("Failed to parse challenge_data.json: " + err.Error()) challengeData = ChallengeDataConfig{} } }) return &challengeData } func getChallengeStagesList() []uint32 { cd := getChallengeData() if len(cd.StagesList) > 0 { return cd.StagesList } var stages []uint32 for id := range cd.Challenges { stages = append(stages, id) } for id := range cd.Tierce { stages = append(stages, id) } sortU32Slice(stages) // dedup out := stages[:0] var last uint32 = ^uint32(0) for _, id := range stages { if id != last { out = append(out, id) last = id } } return out } func getChallengeGroups() []uint32 { cd := getChallengeData() if len(cd.GroupsList) > 0 { return cd.GroupsList } var groups []uint32 for _, c := range cd.Challenges { if c.GroupID != 0 { groups = append(groups, c.GroupID) } } sortU32Slice(groups) out := groups[:0] var last uint32 = ^uint32(0) for _, g := range groups { if g != last { out = append(out, g) last = g } } return out } func groupTotalStars(groupID uint32) uint64 { cd := getChallengeData() n := uint64(0) for _, c := range cd.Challenges { if c.GroupID == groupID { n++ } } return n * 3 } func challengeScore(id uint32) uint32 { switch { case id >= 20000 && id < 30000: return 80000 case id >= 30000 || (id >= 5000 && id < 6000): return 4000 } return 0 } func init() { handle("GetChallenge", func(s *Session, req *goproto.GetChallengeCsReq, rsp *goproto.GetChallengeScRsp) error { for _, groupID := range getChallengeGroups() { rsp.ChallengeGroupList = append(rsp.ChallengeGroupList, &goproto.ChallengeGroup{ GroupId: groupID, TakenStarsCountReward: groupTotalStars(groupID), }) } for _, id := range getChallengeStagesList() { rsp.ChallengeList = append(rsp.ChallengeList, &goproto.Challenge{ ChallengeId: id, Star: 7, TakenReward: 7, RecordId: 1, HGPKMHFPMBJ: false, ScoreTwo: challengeScore(id), ScoreId: challengeScore(id), }) } addMaxLevel := func(level, rType uint32, unlocked bool) { rsp.MaxLevelList = append(rsp.MaxLevelList, &goproto.ChallengeHistoryMaxLevel{ Level: level, HNHCFJJNJCE: unlocked, RewardDisplayType: rType, }) } addMaxLevel(15, 1, true) // Jarilo addMaxLevel(15, 1, false) addMaxLevel(6, 2, true) // Luofu addMaxLevel(6, 2, false) addMaxLevel(12, 3, true) // MoC addMaxLevel(12, 3, false) addMaxLevel(4, 4, true) // Pure Fiction addMaxLevel(4, 4, false) addMaxLevel(4, 5, true) // Apocalyptic Shadow addMaxLevel(4, 5, false) addMaxLevel(6, 6, true) // Anomaly Arbitration addMaxLevel(6, 6, false) for r := uint32(101001); r <= 101015; r++ { addMaxLevel(15, r, true) } for r := uint32(101016); r <= 101021; r++ { addMaxLevel(6, r, true) } for r := uint32(101201); r <= 101212; r++ { addMaxLevel(12, r, true) } for r := uint32(101401); r <= 101404; r++ { addMaxLevel(4, r, true) } for _, r := range []uint32{101713, 101913, 102113} { addMaxLevel(4, r, true) } return nil }) handle("GetCurChallenge", func(s *Session, req *goproto.GetCurChallengeCsReq, rsp *goproto.GetCurChallengeScRsp) error { rsp.Retcode = 0 return nil }) handle("GetChallengePeakData", func(s *Session, req *goproto.GetChallengePeakDataCsReq, rsp *goproto.GetChallengePeakDataScRsp) error { rsp.Retcode = 0 rsp.CurrentPeakGroupId = 1 cd := getChallengeData() type stageEntry struct { id uint32 stage PeakStageData } groups := map[uint32][]stageEntry{} for k, stage := range cd.Peak { var id uint32 if _, err := fmt.Sscanf(k, "%d", &id); err != nil { continue } g := id / 100 groups[g] = append(groups[g], stageEntry{id, stage}) } if len(groups) == 0 { peakGroup := &goproto.ChallengePeakGroup{ PeakGroupId: 1, ObtainedStars: 9, CountOfPeaks: 3, DisableHardMode: false, TakenStarRewards: []uint32{1, 2, 3, 4, 5, 6, 7, 8, 9}, } for peakID := uint32(1); peakID <= 3; peakID++ { peakGroup.Peaks = append(peakGroup.Peaks, &goproto.ChallengePeak{ PeakId: peakID, HasPassed: true, FinishedTargetList: []uint32{1, 2, 3, 4, 5}, }) } rsp.ChallengePeakGroups = append(rsp.ChallengePeakGroups, peakGroup) return nil } groupIDs := sortedU32Keys2(groups) for _, groupID := range groupIDs { stages := groups[groupID] sort.Slice(stages, func(i, j int) bool { return stages[i].id < stages[j].id }) peakGroup := &goproto.ChallengePeakGroup{ PeakGroupId: groupID, CountOfPeaks: uint32(len(stages)), } for _, entry := range stages { stars := uint32(len(entry.stage.Targets)) peakGroup.ObtainedStars += stars for t := uint32(1); t <= stars; t++ { peakGroup.TakenStarRewards = append(peakGroup.TakenStarRewards, peakGroup.ObtainedStars-stars+t) } peakGroup.Peaks = append(peakGroup.Peaks, &goproto.ChallengePeak{ PeakId: entry.id, HasPassed: true, FinishedTargetList: entry.stage.Targets, }) } rsp.ChallengePeakGroups = append(rsp.ChallengePeakGroups, peakGroup) } return nil }) handle("GetCurChallengePeak", func(s *Session, req *goproto.GetCurChallengePeakCsReq, rsp *goproto.GetCurChallengePeakScRsp) error { // reverted to the state of the build that reached Pom-Pom: // empty peak_id (a fabricated peak_id:1 crashed CreateMemberEntityLeader) rsp.Retcode = 0 rsp.HasPassed = true rsp.PeakId = 1 rsp.BossBuffId = 0 return nil }) handle("GetActivityScheduleConfig", func(s *Session, req *goproto.GetActivityScheduleConfigCsReq, rsp *goproto.GetActivityScheduleConfigScRsp) error { rsp.Retcode = 0 add := func(activityID, panelID uint32) { rsp.ScheduleData = append(rsp.ScheduleData, &goproto.ActivityScheduleData{ ActivityId: activityID, PanelId: panelID, BeginTime: 0, EndTime: 1924992000, }) } for id := uint32(1); id <= 100; id++ { add(id, id) } for actID := uint32(21001); actID <= 21030; actID++ { add(actID, 21001) } for i := uint32(1); i <= 8; i++ { add(2100000+i*100+1, 21001) } for _, base := range []uint32{1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000, 20000, 30000, 40000, 50000, 60000} { for offset := uint32(1); offset <= 50; offset++ { add(base+offset, base+offset) } } return nil }) }