package gameserver import ( "fmt" "gac-hkrpg-go/internal/common" pb "gac-hkrpg-go/protocol/proto" ) // The current native BuyExp capture sends capacity, shop weights and level // together. The native before/after capture does not rewrite individual role // property maps. This does not implement all level-dependent combat effects. func cwApplyBaseExperience(catalog *common.CurrencyWarsCatalog, player *pb.NIHNGPKHEPC, shop *pb.POCJDMMANBM, gain uint32) error { previous, err := catalog.PlayerLevel(player.EHNJDIDABHD) if err != nil { return err } plan, err := catalog.PlanBaseExperience(player.EHNJDIDABHD, player.GridFightCurLevelExp, gain, player.NDAINFLOLPJ) if err != nil { return err } row, err := catalog.PlayerLevel(plan.Level) if err != nil { return err } // Preserve already resolved run bonuses, rather than resetting capacity // to the unmodified level table when equipment/augments add slots. capacity := int64(player.AFFCFLFIFPH) + int64(row.AvatarMaxNumber) - int64(previous.AvatarMaxNumber) if capacity <= 0 || uint64(capacity) > uint64(^uint32(0)) { return fmt.Errorf("CW level capacity is outside protocol range") } player.EHNJDIDABHD = plan.Level player.GridFightCurLevelExp = plan.Exp player.AFFCFLFIFPH = uint32(capacity) shop.ABBNGCPECAI = &pb.MONJNCPINKN{} for i, weight := range row.RarityWeights() { shop.ABBNGCPECAI.FIJPICNPKIG = append(shop.ABBNGCPECAI.FIJPICNPKIG, &pb.BOPHDCKJMMN{CKKDAEMFDPA: uint32(i + 1), APAKCBFMCAB: weight}) } return nil } func cwCapacitySync(player *pb.NIHNGPKHEPC) *pb.KFNCAIHIHCM { return &pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_BPCFDPNHIEL{BPCFDPNHIEL: player.AFFCFLFIFPH}} } func init() { cwHandle("GridFightBuyExp", func(s *Session, _ *pb.GridFightBuyExpCsReq, rsp *pb.GridFightBuyExpScRsp) error { rsp.Retcode = uint32(pb.Retcode_RET_GRID_FIGHT_PARAM_NOT_MATCH) catalog, data, err := cwPreparingData(s) if err != nil { PacketLog("CW_BUY_EXP rejected: " + err.Error()) return nil } player, _, shop := cwComponents(data.DHFOFLFCLAH) if player == nil || shop == nil { return nil } row, err := catalog.PlayerLevel(player.EHNJDIDABHD) if err != nil || row.LevelUpExp == 0 || player.EHNJDIDABHD >= player.NDAINFLOLPJ { return nil } cost := player.BEPMODICONA // The run's resolved quote, not a fixed gold price. if player.Gold < cost { rsp.Retcode = uint32(pb.Retcode_RET_GRID_FIGHT_COIN_NOT_ENOUGH) return nil } gain, err := catalog.IntegerRule("GridFight_LevelUpExp") if err != nil || gain <= 0 || uint64(gain) > uint64(^uint32(0)) { PacketLog(fmt.Sprintf("CW_BUY_EXP rejected: missing or invalid GridFight_LevelUpExp rule value=%d error=%v", gain, err)) rsp.Retcode = uint32(pb.Retcode_RET_GRID_FIGHT_CONF_MISS) return nil } if err = cwApplyBaseExperience(catalog, player, shop, uint32(gain)); err != nil { PacketLog("CW_BUY_EXP rejected: " + err.Error()) return nil } player.Gold -= cost if err = s.saveCurrencyWarsData(data, catalog); err != nil { PacketLog("CW_BUY_EXP save failed: " + err.Error()) return nil } notify := cwNotify(cwGoldSync(player.Gold), cwCapacitySync(player), cwShopSync(shop), &pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_PLPHEIBLPDL{PLPHEIBLPDL: &pb.PIHKLPJGEPM{Level: player.EHNJDIDABHD, Exp: player.GridFightCurLevelExp, HFMKLAPEKFA: player.NDAINFLOLPJ}}}) notify.GCPNIHHBGGD[0].GridUpdateSrc = pb.JJDFGMMMCMI_JJDFGMMMCMI_IHEKGIGCGEC if err = cwSend(s, notify); err != nil { return err } rsp.Retcode = 0 PacketLog(fmt.Sprintf("CW_BUY_EXP saved cost=%d gain=%d level=%d exp=%d capacity=%d gold=%d revision=%d", cost, gain, player.EHNJDIDABHD, player.GridFightCurLevelExp, player.AFFCFLFIFPH, player.Gold, s.currencyWarsRevision)) return nil }) }