package mapPlayer import ( actorRemote "f1-game/internal/actor_remote" "f1-game/internal/code" "f1-game/internal/component/redis" "f1-game/internal/constant" "f1-game/internal/data" "f1-game/internal/enum" nameLocal "f1-game/internal/name/local" nameRemote "f1-game/internal/name/remote" "f1-game/internal/pb" "f1-game/internal/types" ao "f1-game/nodes/game/player/asset/origin" mapCall "f1-game/nodes/map/internal/call" "f1-game/nodes/map/internal/db" mapFacade "f1-game/nodes/map/internal/facade" "f1-game/nodes/map/player/exchange" cstring "github.com/cherry-game/cherry/extend/string" cproto "github.com/cherry-game/cherry/net/proto" ) func (p *actorPlayer) initExchange() { p.Local().Register(nameLocal.MapPlayer_ExchangeSlotUnlock, p.exchangeSlotUnlock) p.Local().Register(nameLocal.MapPlayer_ExchangeHistoryPrice, p.exchangeHistoryPrice) p.Local().Register(nameLocal.MapPlayer_ExchangeItem, p.exchangeItem) p.Local().Register(nameLocal.MapPlayer_ExchangeBuyItem, p.exchangeBuyItem) p.Local().Register(nameLocal.MapPlayer_ExchangeQuery, p.exchangeQuery) p.Local().Register(nameLocal.MapPlayer_ExchangeMyItems, p.exchangeMyItems) p.Local().Register(nameLocal.MapPlayer_ExchangeItemUpdate, p.exchangeItemUpdate) p.Local().Register(nameLocal.MapPlayer_ExchangeLog, p.exchangeLog) p.Remote().Register(nameRemote.MapPlayerExchange_ItemDeliver, p.exchangeItemDeliver) p.Remote().Register(nameRemote.MapPlayerExchange_ItemRetrieve, p.exchangeItemRetrieve) p.Remote().Register(nameRemote.MapPlayerExchange_ItemCanceled, p.exchangeBuyItemCanceled) p.Remote().Register(nameRemote.MapPlayerExchange_ItemRemove, p.exchangeBuyItemRemove) } // 交易所货柜解锁 func (p *actorPlayer) exchangeSlotUnlock(session *cproto.Session, req *pb.I32) { actType := enum.ExchangeActType(req.Value) if !enum.IsExchangeActType(actType) { p.ResponseCode(session, code.IllegalArgument) return } exchangeSlotRow, found := data.ExchangeSlot.GetBySlotTypeExchangeType(actType, enum.ExchangeType_Map) if !found { p.ResponseCode(session, code.ConfigNotFound_ExchangeSlot) return } playerTable := db.GetMapPlayerTable(p.playerID) switch actType { case enum.ExchangeActType_Sell: { nowCount := playerTable.Exchange.SellingUnlockedSlots if int(nowCount) >= len(exchangeSlotRow.Cost) { p.ResponseCode(session, code.ExchangeSlotSellMaxLimit) return } unlockCost := exchangeSlotRow.Cost[nowCount] if unlockCost.ID > 0 && unlockCost.Num > 0 { // 检查解锁消耗是否足够,只判断非地图的资源 assets := types.Assets{unlockCost} if _, errCode := actorRemote.GamePlayer.AssetSub(playerTable.GameNodeID, p.playerID, assets, ao.ExchangeSellUnlockCost); code.IsFail(errCode) { p.ResponseCode(session, errCode) return } } // 增加出售货柜解锁数量 playerTable.Exchange.SellingUnlockedSlots++ playerTable.Save2Queue() p.Debug("exchangeSlotUnlock success. actType %v, sellingUnlockedSlots [%d->%d]", actType, nowCount, playerTable.Exchange.SellingUnlockedSlots) p.Response(session, &pb.I32I32{ Key: int32(actType), Value: playerTable.Exchange.SellingUnlockedSlots, }) } case enum.ExchangeActType_Wish: { nowCount := playerTable.Exchange.WishingUnlockedSlots if int(nowCount) >= len(exchangeSlotRow.Cost) { p.ResponseCode(session, code.ExchangeSlotWishMaxLimit) return } unlockCost := exchangeSlotRow.Cost[nowCount] if unlockCost.ID > 0 && unlockCost.Num > 0 { // 检查解锁消耗是否足够,只判断非地图的资源 assets := types.Assets{unlockCost} if _, errCode := actorRemote.GamePlayer.AssetSub(playerTable.GameNodeID, p.playerID, assets, ao.ExchangeWishUnlockCost); code.IsFail(errCode) { p.ResponseCode(session, errCode) return } } // 增加求购货柜解锁数量 playerTable.Exchange.WishingUnlockedSlots++ playerTable.Save2Queue() p.Debug("exchangeSlotUnlock success. actType %v, buyingUnlockedSlots [%d->%d]", actType, nowCount, playerTable.Exchange.WishingUnlockedSlots) p.Response(session, &pb.I32I32{ Key: int32(actType), Value: playerTable.Exchange.WishingUnlockedSlots, }) } } } // 获取物品的历史求购价格 func (p *actorPlayer) exchangeHistoryPrice(session *cproto.Session, req *pb.I32) { itemID := req.Value if itemID < 1 { p.ResponseCode(session, code.IllegalArgument) return } if !data.ExchangeGoods.ContainID(itemID) { p.ResponseCode(session, code.ConfigNotFound_ExchangeGoods) return } historyPrices, errCode := mapCall.Exchange.HistoryPrice(itemID) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } p.Response(session, historyPrices) p.Debug("exchangeHistoryPrice success. itemID %d, historyPrices %v", itemID, historyPrices) } // 出售或求购资源和材料 func (p *actorPlayer) exchangeItem(session *cproto.Session, req *pb.ExchangeItem) { var ( actType = enum.ExchangeActType(req.ActType) assetProto = req.Asset price = req.Price ) if !enum.IsExchangeActType(actType) { p.ResponseCode(session, code.IllegalArgument) return } if assetProto == nil || assetProto.Id < 1 || assetProto.Num < 1 || price < 1 { p.ResponseCode(session, code.IllegalArgument) return } exchangeGoodsRow, found := data.ExchangeGoods.GetByID(assetProto.Id) if !found { p.ResponseCode(session, code.ConfigNotFound_ExchangeGoods) return } if exchangeGoodsRow.ExchangeType != enum.ExchangeType_Map { p.ResponseCode(session, code.IllegalArgument) return } // 地图交易所只交易资源类型的商品 if exchangeGoodsRow.ItemType != enum.ExchangeItemType_Resource { p.ResponseCode(session, code.IllegalArgument) return } // 不允许交易非地图资源 if !enum.IsMapCurrency(assetProto.Id) { p.ResponseCode(session, code.ExchangeItemConfigError) return } // 交易最低手续费不能低于单价 if exchangeGoodsRow.MinFee > exchangeGoodsRow.MinPrice { p.ResponseCode(session, code.ExchangeMinFeeConfigError) return } // 判断物品的单价是否合法 if price < exchangeGoodsRow.MinPrice || price > exchangeGoodsRow.MaxPrice { p.ResponseCode(session, code.ExchangeItemPriceIllegal) return } var ( mapPlayerTable = db.GetMapPlayerTable(p.playerID) playerExchangeItem = &pb.PlayerExchangeItem{ PlayerID: p.playerID, GameNodeID: mapPlayerTable.GameNodeID, ItemInfo: req, } ) switch actType { case enum.ExchangeActType_Sell: // 判断交易数量是否合法 if assetProto.Num < int64(exchangeGoodsRow.MinCount) || assetProto.Num > int64(exchangeGoodsRow.MaxCount) { p.ResponseCode(session, code.ExchangeItemQtyIllegal) return } if !mapPlayerTable.Exchange.IsSlotsEnough(actType) { p.ResponseCode(session, code.ExchangeSlotSellNotEnough) return } // 数量单位 unitQty := exchangeGoodsRow.UnitQty // 总数量 sumCount := assetProto.Num * int64(unitQty) // 出售就判断自身是否有足够的物品 if !mapFacade.Storage.IsEnough(p.playerID, assetProto.Id, sumCount) { p.ResponseCode(session, code.ExchangeCountNotEnough) return } retExchangeDetail, errCode := mapCall.Exchange.ExchangeItem(playerExchangeItem) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } mapPlayerTable.Exchange.Sells.Add(retExchangeDetail.TransactionID) mapPlayerTable.Save2Queue() // 扣除物品 mapFacade.Storage.AssetSubOne(p.playerID, assetProto.Id, sumCount, ao.ExchangeSellItemCost, true) p.Response(session, retExchangeDetail) p.Debug("exchangeItem success. transID [%s]", retExchangeDetail.TransactionID) case enum.ExchangeActType_Wish: if !mapPlayerTable.Exchange.IsSlotsEnough(actType) { p.ResponseCode(session, code.ExchangeSlotWishNotEnough) return } // 计算总价 sumPrice := int64(price) * assetProto.Num var ( asset = types.NewAsset(exchangeGoodsRow.ItemID, sumPrice) assets = types.Assets{&asset} ) // 求购就判断自身是否有足够的交易道具 if errCode := actorRemote.GamePlayer.AssetCheck(mapPlayerTable.GameNodeID, p.playerID, assets); code.IsFail(errCode) { p.ResponseCode(session, errCode) return } // 判断每日求购次数是否已达上限, 如果已达上限则标记为已达上限,不进行匹配 if mapPlayerTable.Exchange.DailyWishCount >= data.Const.ExchangeDailyWishLimit { playerExchangeItem.IsWishLimit = true } retExchangeDetail, errCode := mapCall.Exchange.ExchangeItem(playerExchangeItem) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } mapPlayerTable.Exchange.Wishs.Add(retExchangeDetail.TransactionID) mapPlayerTable.Save2Queue() // 扣除求购消耗 if _, errCode := actorRemote.GamePlayer.AssetSub(mapPlayerTable.GameNodeID, p.playerID, assets, ao.ExchangeWishItemCost); code.IsFail(errCode) { p.Warn("exchangeItem AssetSub errCode [%d], transID [%s]", errCode, retExchangeDetail.TransactionID) p.ResponseCode(session, errCode) return } p.Response(session, retExchangeDetail) p.Debug("exchangeItem success. transID [%s]", retExchangeDetail.TransactionID) } } // 购买物品 func (p *actorPlayer) exchangeBuyItem(session *cproto.Session, req *pb.I32Str) { var ( transID = req.Value // 交易流水号 count = req.Key // 数量 ) if cstring.IsBlank(transID) || count < 1 { p.ResponseCode(session, code.IllegalArgument) return } mapPlayerTable := db.GetMapPlayerTable(p.playerID) // 判断是不是自己的交易流水号 if mapPlayerTable.Exchange.IsOwnTransaction(transID) { p.ResponseCode(session, code.IllegalArgument) return } // 判断每日购买次数是否已达上限 if mapPlayerTable.Exchange.DailyBuyCount >= data.Const.ExchangeDailyBuyLimit { p.ResponseCode(session, code.ExchangeDailyBuyCountLimit) return } buyReq := &pb.PlayerExchangeBuyItem{ PlayerID: p.playerID, GameNodeID: mapPlayerTable.GameNodeID, TransID: transID, Count: int64(count), } retResult, errCode := mapCall.Exchange.BuyItem(buyReq) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } if code.IsOK(retResult.Code) { // 记录每日购买次数 mapPlayerTable.Exchange.DailyBuyCount++ mapPlayerTable.Save2Queue() p.Debug("exchangeBuyItem success. transID [%s], count [%d]", transID, count) } p.Response(session, retResult) } // 根据条件查询地图交易所的商品列表 func (p *actorPlayer) exchangeQuery(session *cproto.Session, req *pb.ExchangeQueryFilter) { pageNum := req.PageNum // 页数 if pageNum < 1 { p.ResponseCode(session, code.IllegalArgument) return } itemDetails, errCode := mapCall.Exchange.QueryItem(req) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } p.Response(session, itemDetails) p.Debug("exchangeQuery success. itemDetail len [%d]", len(itemDetails.List)) } // 获取自己的商品信息 func (p *actorPlayer) exchangeMyItems(session *cproto.Session, req *pb.I32) { actType := enum.ExchangeActType(req.Value) if !enum.IsExchangeActType(actType) { p.ResponseCode(session, code.IllegalArgument) return } mapPlayerTable := db.GetMapPlayerTable(p.playerID) rsp := &pb.MyExchangeItems{ ActType: req.Value, UnlockedSlots: mapPlayerTable.Exchange.GetUnlockSlots(actType), } transIDList := mapPlayerTable.Exchange.GetItemsByActType(actType) if transIDList.IsEmpty() { p.Response(session, rsp) return } retItemList, errCode := mapCall.Exchange.MyItems(p.playerID, actType, transIDList) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } rsp.List = retItemList.List p.Response(session, rsp) p.Debug("exchangeMyItems success. actType [%d], itemList [%+v]", actType, transIDList) } // 手动更新商品状态,包括下架,重新下架,取回 func (p *actorPlayer) exchangeItemUpdate(session *cproto.Session, req *pb.I32Str) { var ( updateType = enum.ExchangeUpdateType(req.Key) // 更新类型 transID = req.Value // 流水号 ) if !enum.IsExchangeUpdateType(updateType) { p.ResponseCode(session, code.IllegalArgument) return } if cstring.IsBlank(transID) { p.ResponseCode(session, code.IllegalArgument) return } mapPlayerTable := db.GetMapPlayerTable(p.playerID) actType := mapPlayerTable.Exchange.GetActTypeByTransID(transID) if actType == 0 { p.ResponseCode(session, code.ExchangeItemNotFound) return } // 是否已达每日求购次数上限做一个标记 isWishLimit := false if updateType == enum.ExchangeUpdateType_ReList { // 判断每日求购次数是否已达上限, 如果已达上限则标记为已达上限,不进行匹配 if mapPlayerTable.Exchange.DailyWishCount >= data.Const.ExchangeDailyWishLimit { isWishLimit = true } } errCode := mapCall.Exchange.ItemUpdate(p.playerID, updateType, actType, transID, isWishLimit) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } // 只有取回需要删除记录 if updateType == enum.ExchangeUpdateType_Retrieve { switch actType { case enum.ExchangeActType_Wish: mapPlayerTable.Exchange.Wishs.RemoveValue(transID) case enum.ExchangeActType_Sell: mapPlayerTable.Exchange.Sells.RemoveValue(transID) } mapPlayerTable.Save2Queue() } p.ResponseCode(session, code.OK) p.Debug("exchangeItemUpdate success. updateType [%d], transID [%s]", updateType, transID) } // 获取交易日志 func (p *actorPlayer) exchangeLog(session *cproto.Session, req *pb.I32I64) { var ( logType = enum.ExchangeLogType(req.Key) // 交易日志类型 lastLogID = req.Value // 最近的日志ID ) if lastLogID < 0 { p.ResponseCode(session, code.IllegalArgument) return } if !enum.IsExchangeLogType(logType) { p.ResponseCode(session, code.IllegalArgument) return } logList := redis.Game.GetMapExchangeLogList(p.App().NodeID(), p.playerID, logType, lastLogID) if logList == nil { p.Response(session, &pb.ExchangeLogList{}) return } p.Response(session, logList) p.Debug("exchangeLog success. logType [%d], lastLogID [%d], logList size [%d]", logType, lastLogID, len(logList.List)) } // 发货接口 func (p *actorPlayer) exchangeItemDeliver(req *pb.PlayerExchangeItemDeliver) { // 自己是买方 if req.IsBuyer { exchange.Service().DeliverToBuyer(p.playerID, req) } else { // 自己是卖方 exchange.Service().DeliverToSeller(p.playerID, req) } } // 取回 func (p *actorPlayer) exchangeItemRetrieve(req *pb.ExchangeDetail) { var ( actType = enum.ExchangeActType(req.ActType) transID = req.TransactionID mapPlayerTable = db.GetMapPlayerTable(p.playerID) asset = req.Asset exchangeGoodsRow, _ = data.ExchangeGoods.GetByID(asset.Id) ) switch actType { case enum.ExchangeActType_Sell: { // 数量单位 unitQty := exchangeGoodsRow.UnitQty // 总数量 sumCount := asset.Num * int64(unitQty) // 返还出售资源 mapFacade.Storage.AssetAddOne(p.playerID, asset.Id, sumCount, ao.ExchangeItemSellRetrieve, true) // 删除出售关联数据 mapPlayerTable.Exchange.Sells.RemoveValue(transID) mapPlayerTable.Save2Queue() mailParams := []string{ cstring.ToString(asset.Id), cstring.ToString(sumCount), } actorRemote.GameMail.SendMail(mapPlayerTable.GameNodeID, p.playerID, constant.MailID_MapExchangeRetrieve, nil, mailParams) p.Debug("exchangeItemRetrieve success. transID [%s], asset [%+v], sumCount [%d]", transID, asset, sumCount) } case enum.ExchangeActType_Wish: { // 交易物品ID itemID := exchangeGoodsRow.ItemID // 单价 itemPrice := req.Price // 总价 sumItemPrice := asset.Num * int64(itemPrice) wishAssets := types.Assets{} wishAssets.Add(itemID, int64(sumItemPrice)) // 返还求购资源 actorRemote.GamePlayer.AssetAddAsync(mapPlayerTable.GameNodeID, p.playerID, wishAssets, ao.ExchangeItemWishRetrieve) // 删除求购关联数据 mapPlayerTable.Exchange.Wishs.RemoveValue(transID) mapPlayerTable.Save2Queue() mailParams := []string{ cstring.ToString(itemID), cstring.ToString(sumItemPrice), } actorRemote.GameMail.SendMail(mapPlayerTable.GameNodeID, p.playerID, constant.MailID_MapExchangeWishCancel, nil, mailParams) p.Debug("exchangeItemRetrieve success. transID [%s], wishAssets [%+v]", transID, wishAssets) } } } // 系统通知商品下架 func (p *actorPlayer) exchangeBuyItemCanceled(req *pb.ExchangeDetail) { var ( actType = enum.ExchangeActType(req.ActType) mapPlayerTable = db.GetMapPlayerTable(p.playerID) asset = req.Asset exchangeGoodsRow, _ = data.ExchangeGoods.GetByID(asset.Id) ) // 求购需要发邮件通知 if actType == enum.ExchangeActType_Wish { // 求购总数量 sumCount := asset.Num * int64(exchangeGoodsRow.UnitQty) mailParams := []string{ cstring.ToString(asset.Id), cstring.ToString(sumCount), } actorRemote.GameMail.SendMail(mapPlayerTable.GameNodeID, p.playerID, constant.MailID_MapExchangeWishTimeout, nil, mailParams) } p.Debug("exchangeBuyItemCanceled success. actType [%d] transID [%s]", actType, req.TransactionID) } // 同步删除交易流水号 func (p *actorPlayer) exchangeBuyItemRemove(req *pb.I32Str) { var ( transID = req.Value actType = enum.ExchangeActType(req.Key) mapPlayerTable = db.GetMapPlayerTable(p.playerID) ) switch actType { case enum.ExchangeActType_Sell: mapPlayerTable.Exchange.Sells.RemoveValue(transID) mapPlayerTable.Save2Queue() case enum.ExchangeActType_Wish: mapPlayerTable.Exchange.Wishs.RemoveValue(transID) mapPlayerTable.Save2Queue() } p.Debug("exchangeBuyItemRemove success. actType [%d], transID [%s]", actType, transID) }