package gameserver import ( "gac-hkrpg-go/internal/common" goproto "gac-hkrpg-go/protocol/proto" "strings" "unicode/utf16" "unicode/utf8" ) func commonToLineupAvatars(f *common.FreesrData) []*goproto.LineupAvatar { return common.ToLineupAvatars(f) } func init() { handle("GetAllLineupData", func(s *Session, _ *goproto.GetAllLineupDataCsReq, rsp *goproto.GetAllLineupDataScRsp) error { var err error rsp.LineupList, err = allPresetLineups(s) if p := s.JSON(); p != nil { rsp.CurIndex = p.ActiveTeamPreset } return err }) handle("GetCurLineupData", func(s *Session, _ *goproto.GetCurLineupDataCsReq, rsp *goproto.GetCurLineupDataScRsp) error { rsp.Lineup = s.techniqueLineup() return nil }) handle("JoinLineup", func(s *Session, req *goproto.JoinLineupCsReq, rsp *goproto.JoinLineupScRsp) error { code, err := s.presetEditCode(req.Index, req.ExtraLineupType, req.IsVirtual) rsp.Retcode = code if err != nil || code != 0 { return err } saved := s.JSON().TeamPreset(req.Index) saved.Avatars[req.Slot] = req.BaseAvatarId if code := validatePresetMembers(s.JSON(), saved.Avatars); code != 0 { rsp.Retcode = code return nil } return s.savePreset(req.Index, saved) }) handle("ReplaceLineup", func(s *Session, req *goproto.ReplaceLineupCsReq, rsp *goproto.ReplaceLineupScRsp) error { code, err := s.presetEditCode(req.Index, req.ExtraLineupType, req.IsVirtual) rsp.Retcode = code if err != nil || code != 0 { return err } saved := s.JSON().TeamPreset(req.Index) saved.Avatars = map[uint32]uint32{} for _, a := range req.LineupSlotList { saved.Avatars[a.Slot] = a.Id } if code := validatePresetMembers(s.JSON(), saved.Avatars); code != 0 { rsp.Retcode = code return nil } if req.Index == s.JSON().ActiveTeamPreset && len(common.ToLineupAvatars(&common.FreesrData{Lineups: saved.Avatars, Avatars: s.JSON().Avatars})) == 0 { rsp.Retcode = uint32(goproto.Retcode_RET_LINEUP_IS_EMPTY) return nil } saved.LeaderSlot = req.LeaderSlot if saved.Avatars[saved.LeaderSlot] == 0 { for _, slot := range sortedU32Keys2(saved.Avatars) { if saved.Avatars[slot] != 0 { saved.LeaderSlot = slot break } } } return s.savePreset(req.Index, saved) }) handle("SwapLineup", func(s *Session, req *goproto.SwapLineupCsReq, rsp *goproto.SwapLineupScRsp) error { code, err := s.presetEditCode(req.Index, req.ExtraLineupType, req.IsVirtual) rsp.Retcode = code if err != nil || code != 0 { return err } saved := s.JSON().TeamPreset(req.Index) src, dst := req.MPIEGFCNBEN, req.ENBIBCPPLDC saved.Avatars[src], saved.Avatars[dst] = saved.Avatars[dst], saved.Avatars[src] if code := validatePresetMembers(s.JSON(), saved.Avatars); code != 0 { rsp.Retcode = code return nil } if saved.LeaderSlot == src { saved.LeaderSlot = dst } else if saved.LeaderSlot == dst { saved.LeaderSlot = src } return s.savePreset(req.Index, saved) }) handle("SwitchLineupIndex", func(s *Session, req *goproto.SwitchLineupIndexCsReq, rsp *goproto.SwitchLineupIndexScRsp) error { code, err := s.presetEditCode(req.Index, goproto.ExtraLineupType_ExtraLineupType_LineupNone, false) rsp.Retcode = code if err != nil || code != 0 { return err } if s.ChallengeRun != nil { rsp.Retcode = uint32(goproto.Retcode_RET_LINEUP_NOT_ALLOW_EDIT) return nil } p := s.JSON() saved := p.TeamPreset(req.Index) tmp := *p tmp.Lineups = saved.Avatars if len(common.ToLineupAvatars(&tmp)) == 0 { rsp.Retcode = uint32(goproto.Retcode_RET_LINEUP_IS_EMPTY) return nil } // Retain the outgoing team, including edits made through existing APIs. if err := p.SaveTeamPreset(p.ActiveTeamPreset, p.TeamPreset(p.ActiveTeamPreset)); err != nil { return err } previous := p.ActiveTeamPreset p.ActiveTeamPreset = req.Index if err := p.SaveTeamPreset(req.Index, saved); err != nil { p.ActiveTeamPreset = previous return err } rsp.Index = req.Index return s.publishPreset(req.Index, saved) }) handle("SetLineupName", func(s *Session, req *goproto.SetLineupNameCsReq, rsp *goproto.SetLineupNameScRsp) error { code, err := s.presetEditCode(req.Index, goproto.ExtraLineupType_ExtraLineupType_LineupNone, false) rsp.Retcode = code if err != nil || code != 0 { return err } rules, err := common.LoadTeamPresetRules() if err != nil { return err } name := strings.TrimSpace(req.Name) if !utf8.ValidString(name) || name == "" || len(utf16.Encode([]rune(name))) > rules.MaxNameLength { rsp.Retcode = uint32(goproto.Retcode_RET_LINEUP_NAME_FORMAT_ERROR) return nil } saved := s.JSON().TeamPreset(req.Index) saved.Name = name rsp.Index = req.Index rsp.Name = name return s.savePreset(req.Index, saved) }) handle("MarkPresetLineup", func(s *Session, req *goproto.MarkPresetLineupCsReq, rsp *goproto.MarkPresetLineupScRsp) error { code, err := s.presetEditCode(req.Index, goproto.ExtraLineupType_ExtraLineupType_LineupNone, false) rsp.Retcode = code if err != nil || code != 0 { return err } saved := s.JSON().TeamPreset(req.Index) saved.Favourite = req.IECKKCHJMLP rsp.Index = req.Index rsp.IECKKCHJMLP = req.IECKKCHJMLP return s.savePreset(req.Index, saved) }) handle("QuitLineup", func(s *Session, req *goproto.QuitLineupCsReq, rsp *goproto.QuitLineupScRsp) error { code, err := s.presetEditCode(req.Index, req.ExtraLineupType, req.IsVirtual) rsp.Retcode = code if err != nil || code != 0 { return err } saved := s.JSON().TeamPreset(req.Index) for slot, id := range saved.Avatars { if id == req.BaseAvatarId { delete(saved.Avatars, slot) } } var nonempty bool for _, id := range saved.Avatars { if id != 0 { nonempty = true } } if !nonempty && req.Index == s.JSON().ActiveTeamPreset { rsp.Retcode = uint32(goproto.Retcode_RET_LINEUP_ONLY_ONE_MEMBER) return nil } if saved.Avatars[saved.LeaderSlot] == 0 { for _, slot := range sortedU32Keys2(saved.Avatars) { if saved.Avatars[slot] != 0 { saved.LeaderSlot = slot break } } } return s.savePreset(req.Index, saved) }) handle("GetLineupAvatarData", func(s *Session, _ *goproto.GetLineupAvatarDataCsReq, rsp *goproto.GetLineupAvatarDataScRsp) error { if lineup := s.techniqueLineup(); lineup != nil { for _, a := range lineup.AvatarList { rsp.AvatarDataList = append(rsp.AvatarDataList, &goproto.LineupAvatarData{AvatarType: a.AvatarType, Hp: a.Hp, Id: a.Id}) } } return nil }) handle("ChangeLineupLeader", func(s *Session, req *goproto.ChangeLineupLeaderCsReq, rsp *goproto.ChangeLineupLeaderScRsp) error { if lineup := s.techniqueLineup(); lineup != nil { for _, a := range lineup.AvatarList { if a.Slot == req.Slot { s.leaderSlot = req.Slot rsp.Slot = req.Slot if s.ChallengeRun == nil { p := s.JSON() saved := p.TeamPreset(p.ActiveTeamPreset) saved.LeaderSlot = req.Slot return p.SaveTeamPreset(p.ActiveTeamPreset, saved) } return nil } } } rsp.Retcode = uint32(goproto.Retcode_RET_LINEUP_NOT_VALID_LEADER) return nil }) } // refreshLineup mirrors refresh_lineup: SceneGroupRefreshScNotify + SyncLineupNotify. func refreshLineup(s *Session) error { player := s.JSON() if player == nil { return nil } lineup := s.techniqueLineup() notify := &goproto.SceneGroupRefreshScNotify{ FloorId: player.Scene.FloorID, DimensionId: 0, } group := &goproto.SceneGroupRefreshInfo{ GroupId: 0, State: 0, GroupRefreshType: goproto.SceneGroupRefreshType_SCENE_GROUP_REFRESH_TYPE_LOADED, } for _, slot := range sortedU32Keys2(s.partyActorSlots) { if player.Lineups[slot] == 0 { group.RefreshEntity = append(group.RefreshEntity, &goproto.SceneEntityRefreshInfo{RefreshType: &goproto.SceneEntityRefreshInfo_DeleteEntity{DeleteEntity: slot + 1}}) } } s.partyActorSlots = map[uint32]bool{} for _, slot := range player.SortedLineupKeys() { avatarID := player.Lineups[slot] if avatarID == 0 { continue } s.partyActorSlots[slot] = true group.RefreshEntity = append(group.RefreshEntity, &goproto.SceneEntityRefreshInfo{ RefreshType: &goproto.SceneEntityRefreshInfo_AddEntity{ AddEntity: &goproto.SceneEntityInfo{ EntityId: slot + 1, GroupId: 0, InstId: 0, Entity: &goproto.SceneEntityInfo_Actor{ Actor: &goproto.SceneActorInfo{ AvatarType: goproto.AvatarType_AVATAR_FORMAL_TYPE, BaseAvatarId: avatarID, MapLayer: 0, Uid: 25, }, }, }, }, }) } notify.GroupRefreshInfo = append(notify.GroupRefreshInfo, group) if err := s.Send(notify); err != nil { return err } return s.Send(&goproto.SyncLineupNotify{Lineup: lineup}) }