package player import ( "f1-game/internal/code" "f1-game/internal/data" nameLocal "f1-game/internal/name/local" nameRemote "f1-game/internal/name/remote" "f1-game/internal/pb" "f1-game/internal/types" "f1-game/nodes/game/player/asset" ao "f1-game/nodes/game/player/asset/origin" "f1-game/nodes/game/player/item" cproto "github.com/cherry-game/cherry/net/proto" ) func (p *actorPlayer) initItem() { p.Local().Register(nameLocal.Player_ItemUse, p.itemUse) p.Local().Register(nameLocal.Player_ItemSale, p.itemSale) p.Local().Register(nameLocal.Player_ItemCraft, p.itemCraft) p.Local().Register(nameLocal.Player_ItemMedicineUse, p.itemMedicineUse) } func (p *actorPlayer) itemUse(session *cproto.Session, req *pb.ItemUseRequest) { if req.UseNum < 1 { p.ResponseCode(session, code.IllegalArgument) return } // 使用的必须是宝箱 chestRow, found := data.ItemChest.GetByID(req.ItemID) if !found { p.Warn("[Chest] config not found. id = %d", req.ItemID) p.ResponseCode(session, code.ConfigNotFound_ItemChest) return } // 超出单次使用数量上限 if req.UseNum > data.Const.ItemChestSingleLimit { p.ResponseCode(session, code.ItemUseOnceLimit) return } // 消耗的道具是否足够 if errCode := asset.Service().Enough(p.playerID, req.ItemID, req.UseNum); code.IsFail(errCode) { p.ResponseCode(session, errCode) return } // 使用道具,获取添加的资源 addAssets, errCode := item.Service().Use(p.playerID, req, chestRow.DropPoolID, chestRow.UseType) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } // 检查将要添加的资产 errCode = asset.Service().Check(addAssets) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } // 先扣除消耗道具 _, errCode = asset.Service().Sub(p.playerID, req.ItemID, req.UseNum, ao.None) if code.IsFail(errCode) { p.ResponseCode(session, code.ItemDecreaseFail) return } // 添加资源 retAssets, errCode := asset.Service().Adds(p.playerID, addAssets, ao.UseItem, true) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } // 返回奖励列表(用于展示) rsp := retAssets.ToAssetListProto() p.Response(session, &rsp) p.Debug("[itemUse] result. req = %v, rewards = %v", req, rsp.List) } // itemSale 道具出售 func (p *actorPlayer) itemSale(session *cproto.Session, req *pb.Asset) { if req.Num < 1 { req.Num = 1 } itemRow, found := data.Item.GetByID(req.Id) if !found { p.ResponseCode(session, code.ConfigNotFound_Item) return } if !itemRow.CanSale || len(itemRow.SalePrice) < 1 { p.ResponseCode(session, code.ItemNotForSell) return } _, errCode := asset.Service().Sub(p.playerID, req.Id, req.Num, ao.None) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } rewards := itemRow.GetItemSalePrice(req.Num) retAssets, errCode := asset.Service().Adds(p.playerID, rewards, ao.None, true) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } rsp := retAssets.ToAssetListProto() p.Response(session, &rsp) } // itemCraft 道具合成 func (p *actorPlayer) itemCraft(session *cproto.Session, req *pb.I32I32List) { list := req.List if len(list) < 1 { p.ResponseCode(session, code.ItemCraftEmpty) return } var ( rewards = types.Assets{} tempCosts = map[int32]int64{} // 暂存消耗,key为道具ID,value为消耗数量 playerID = p.playerID ) for _, kv := range list { var ( itemID = kv.Key craftNum = int64(kv.Value) ) if craftNum < 1 { p.ResponseCode(session, code.IllegalArgument) return } // 计算合成路径和所需材料,直接修改tempCosts errCode := p.calculateCraftCostsWithTemp(playerID, itemID, craftNum, tempCosts) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } // 添加到奖励列表 rewards.Add(itemID, craftNum) } // 将暂存的消耗转换为Assets costs := types.Assets{} for itemID, num := range tempCosts { costs.Add(itemID, num) } // 扣除消耗道具 _, errCode := asset.Service().Subs(playerID, costs, ao.ItemCraftCost) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } // 添加合成材料 _, errCode = asset.Service().Adds(playerID, rewards, ao.ItemCraft, true) p.ResponseCode(session, errCode) } // calculateCraftCostsWithTemp 简化版计算合成所需的所有基础材料,直接修改tempCosts func (p *actorPlayer) calculateCraftCostsWithTemp(playerID int64, itemID int32, needNum int64, tempCosts map[int32]int64) int32 { // 直接计算需要消耗的材料 var ( currentItemID = itemID currentNeedNum = needNum ) for { // 检查该道具是否可以合成 itemCraftRow, found := data.ItemCraft.GetByID(currentItemID) if !found { return code.ConfigNotFound_ItemCraft } // 没有合成消耗不能合成 if itemCraftRow.CostItemID <= 0 { return code.ItemCraftNotCan } // 不能合成自己 if itemCraftRow.CostItemID == currentItemID { return code.ItemCraftSelf } // 获取当前道具的库存,减去已经暂存的消耗 originalHasNum := asset.Service().Get(playerID, itemCraftRow.CostItemID) tempConsumed := tempCosts[itemCraftRow.CostItemID] hasNum := originalHasNum - tempConsumed // 如果库存不足,直接返回错误 if hasNum < 0 { return code.ItemNotEnough } // 计算可以合成的数量 canCraftNum := hasNum / itemCraftRow.CostItemNum // 计算总需要消耗的材料数量 totalNeedNum := currentNeedNum * itemCraftRow.CostItemNum // 如果当前材料足够,直接添加到tempCosts中并结束 if canCraftNum >= currentNeedNum { tempCosts[itemCraftRow.CostItemID] += totalNeedNum return code.OK } // 当前材料不足,需要合成更多 // 1. 先使用现有的材料 if canCraftNum > 0 { useNum := canCraftNum * itemCraftRow.CostItemNum tempCosts[itemCraftRow.CostItemID] += useNum } // 2. 计算还需要合成的材料数量,并继续向下查找 remainingNeed := currentNeedNum - canCraftNum if remainingNeed > 0 { // 继续处理下一级合成 currentItemID = itemCraftRow.CostItemID currentNeedNum = remainingNeed * itemCraftRow.CostItemNum } else { // 没有剩余需要,结束 return code.OK } } } // 队伍药品使用 func (p *actorPlayer) itemMedicineUse(session *cproto.Session, req *pb.I32I32) { var ( teamID = req.Key medicineID = req.Value itemTable = p.ItemTable() playerTable = p.PlayerTable() ) medicineRow, found := data.ItemMedicine.GetByMedicineID(medicineID) if !found { p.ResponseCode(session, code.ConfigNotFound_Medicine) return } // 检测道具是否足够 errCode := asset.Service().Enough(p.playerID, medicineID, 1) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } curUseNum, exist := itemTable.MedicineMap[medicineID] if !exist { curUseNum = 0 } // 使用次数已达上限 if curUseNum >= medicineRow.DayUseNumLimit { p.ResponseCode(session, code.ItemUseNumLimit) return } // 调用map使用药品 useReq := &pb.I32I32{ Key: teamID, Value: medicineID, } errCode = p.mapPlayerCallWait(playerTable.Map.MapNodeID, nameRemote.MapPlayer_UseMedicine, useReq) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } // 扣除资源 asset.Service().Sub(p.playerID, medicineID, 1, ao.ItemMedicineUse) curUseNum++ itemTable.MedicineMap[medicineID] = curUseNum itemTable.Save2Queue() resp := &pb.I32I32{ Key: medicineID, Value: curUseNum, } p.Response(session, resp) }