player_item.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. package player
  2. import (
  3. "f1-game/internal/code"
  4. "f1-game/internal/data"
  5. nameLocal "f1-game/internal/name/local"
  6. nameRemote "f1-game/internal/name/remote"
  7. "f1-game/internal/pb"
  8. "f1-game/internal/types"
  9. "f1-game/nodes/game/player/asset"
  10. ao "f1-game/nodes/game/player/asset/origin"
  11. "f1-game/nodes/game/player/item"
  12. cproto "github.com/cherry-game/cherry/net/proto"
  13. )
  14. func (p *actorPlayer) initItem() {
  15. p.Local().Register(nameLocal.Player_ItemUse, p.itemUse)
  16. p.Local().Register(nameLocal.Player_ItemSale, p.itemSale)
  17. p.Local().Register(nameLocal.Player_ItemCraft, p.itemCraft)
  18. p.Local().Register(nameLocal.Player_ItemMedicineUse, p.itemMedicineUse)
  19. }
  20. func (p *actorPlayer) itemUse(session *cproto.Session, req *pb.ItemUseRequest) {
  21. if req.UseNum < 1 {
  22. p.ResponseCode(session, code.IllegalArgument)
  23. return
  24. }
  25. // 使用的必须是宝箱
  26. chestRow, found := data.ItemChest.GetByID(req.ItemID)
  27. if !found {
  28. p.Warn("[Chest] config not found. id = %d", req.ItemID)
  29. p.ResponseCode(session, code.ConfigNotFound_ItemChest)
  30. return
  31. }
  32. // 超出单次使用数量上限
  33. if req.UseNum > data.Const.ItemChestSingleLimit {
  34. p.ResponseCode(session, code.ItemUseOnceLimit)
  35. return
  36. }
  37. // 消耗的道具是否足够
  38. if errCode := asset.Service().Enough(p.playerID, req.ItemID, req.UseNum); code.IsFail(errCode) {
  39. p.ResponseCode(session, errCode)
  40. return
  41. }
  42. // 使用道具,获取添加的资源
  43. addAssets, errCode := item.Service().Use(p.playerID, req, chestRow.DropPoolID, chestRow.UseType)
  44. if code.IsFail(errCode) {
  45. p.ResponseCode(session, errCode)
  46. return
  47. }
  48. // 检查将要添加的资产
  49. errCode = asset.Service().Check(addAssets)
  50. if code.IsFail(errCode) {
  51. p.ResponseCode(session, errCode)
  52. return
  53. }
  54. // 先扣除消耗道具
  55. _, errCode = asset.Service().Sub(p.playerID, req.ItemID, req.UseNum, ao.None)
  56. if code.IsFail(errCode) {
  57. p.ResponseCode(session, code.ItemDecreaseFail)
  58. return
  59. }
  60. // 添加资源
  61. retAssets, errCode := asset.Service().Adds(p.playerID, addAssets, ao.UseItem, true)
  62. if code.IsFail(errCode) {
  63. p.ResponseCode(session, errCode)
  64. return
  65. }
  66. // 返回奖励列表(用于展示)
  67. rsp := retAssets.ToAssetListProto()
  68. p.Response(session, &rsp)
  69. p.Debug("[itemUse] result. req = %v, rewards = %v", req, rsp.List)
  70. }
  71. // itemSale 道具出售
  72. func (p *actorPlayer) itemSale(session *cproto.Session, req *pb.Asset) {
  73. if req.Num < 1 {
  74. req.Num = 1
  75. }
  76. itemRow, found := data.Item.GetByID(req.Id)
  77. if !found {
  78. p.ResponseCode(session, code.ConfigNotFound_Item)
  79. return
  80. }
  81. if !itemRow.CanSale || len(itemRow.SalePrice) < 1 {
  82. p.ResponseCode(session, code.ItemNotForSell)
  83. return
  84. }
  85. _, errCode := asset.Service().Sub(p.playerID, req.Id, req.Num, ao.None)
  86. if code.IsFail(errCode) {
  87. p.ResponseCode(session, errCode)
  88. return
  89. }
  90. rewards := itemRow.GetItemSalePrice(req.Num)
  91. retAssets, errCode := asset.Service().Adds(p.playerID, rewards, ao.None, true)
  92. if code.IsFail(errCode) {
  93. p.ResponseCode(session, errCode)
  94. return
  95. }
  96. rsp := retAssets.ToAssetListProto()
  97. p.Response(session, &rsp)
  98. }
  99. // itemCraft 道具合成
  100. func (p *actorPlayer) itemCraft(session *cproto.Session, req *pb.I32I32List) {
  101. list := req.List
  102. if len(list) < 1 {
  103. p.ResponseCode(session, code.ItemCraftEmpty)
  104. return
  105. }
  106. var (
  107. rewards = types.Assets{}
  108. tempCosts = map[int32]int64{} // 暂存消耗,key为道具ID,value为消耗数量
  109. playerID = p.playerID
  110. )
  111. for _, kv := range list {
  112. var (
  113. itemID = kv.Key
  114. craftNum = int64(kv.Value)
  115. )
  116. if craftNum < 1 {
  117. p.ResponseCode(session, code.IllegalArgument)
  118. return
  119. }
  120. // 计算合成路径和所需材料,直接修改tempCosts
  121. errCode := p.calculateCraftCostsWithTemp(playerID, itemID, craftNum, tempCosts)
  122. if code.IsFail(errCode) {
  123. p.ResponseCode(session, errCode)
  124. return
  125. }
  126. // 添加到奖励列表
  127. rewards.Add(itemID, craftNum)
  128. }
  129. // 将暂存的消耗转换为Assets
  130. costs := types.Assets{}
  131. for itemID, num := range tempCosts {
  132. costs.Add(itemID, num)
  133. }
  134. // 扣除消耗道具
  135. _, errCode := asset.Service().Subs(playerID, costs, ao.ItemCraftCost)
  136. if code.IsFail(errCode) {
  137. p.ResponseCode(session, errCode)
  138. return
  139. }
  140. // 添加合成材料
  141. _, errCode = asset.Service().Adds(playerID, rewards, ao.ItemCraft, true)
  142. p.ResponseCode(session, errCode)
  143. }
  144. // calculateCraftCostsWithTemp 简化版计算合成所需的所有基础材料,直接修改tempCosts
  145. func (p *actorPlayer) calculateCraftCostsWithTemp(playerID int64, itemID int32, needNum int64, tempCosts map[int32]int64) int32 {
  146. // 直接计算需要消耗的材料
  147. var (
  148. currentItemID = itemID
  149. currentNeedNum = needNum
  150. )
  151. for {
  152. // 检查该道具是否可以合成
  153. itemCraftRow, found := data.ItemCraft.GetByID(currentItemID)
  154. if !found {
  155. return code.ConfigNotFound_ItemCraft
  156. }
  157. // 没有合成消耗不能合成
  158. if itemCraftRow.CostItemID <= 0 {
  159. return code.ItemCraftNotCan
  160. }
  161. // 不能合成自己
  162. if itemCraftRow.CostItemID == currentItemID {
  163. return code.ItemCraftSelf
  164. }
  165. // 获取当前道具的库存,减去已经暂存的消耗
  166. originalHasNum := asset.Service().Get(playerID, itemCraftRow.CostItemID)
  167. tempConsumed := tempCosts[itemCraftRow.CostItemID]
  168. hasNum := originalHasNum - tempConsumed
  169. // 如果库存不足,直接返回错误
  170. if hasNum < 0 {
  171. return code.ItemNotEnough
  172. }
  173. // 计算可以合成的数量
  174. canCraftNum := hasNum / itemCraftRow.CostItemNum
  175. // 计算总需要消耗的材料数量
  176. totalNeedNum := currentNeedNum * itemCraftRow.CostItemNum
  177. // 如果当前材料足够,直接添加到tempCosts中并结束
  178. if canCraftNum >= currentNeedNum {
  179. tempCosts[itemCraftRow.CostItemID] += totalNeedNum
  180. return code.OK
  181. }
  182. // 当前材料不足,需要合成更多
  183. // 1. 先使用现有的材料
  184. if canCraftNum > 0 {
  185. useNum := canCraftNum * itemCraftRow.CostItemNum
  186. tempCosts[itemCraftRow.CostItemID] += useNum
  187. }
  188. // 2. 计算还需要合成的材料数量,并继续向下查找
  189. remainingNeed := currentNeedNum - canCraftNum
  190. if remainingNeed > 0 {
  191. // 继续处理下一级合成
  192. currentItemID = itemCraftRow.CostItemID
  193. currentNeedNum = remainingNeed * itemCraftRow.CostItemNum
  194. } else {
  195. // 没有剩余需要,结束
  196. return code.OK
  197. }
  198. }
  199. }
  200. // 队伍药品使用
  201. func (p *actorPlayer) itemMedicineUse(session *cproto.Session, req *pb.I32I32) {
  202. var (
  203. teamID = req.Key
  204. medicineID = req.Value
  205. itemTable = p.ItemTable()
  206. playerTable = p.PlayerTable()
  207. )
  208. medicineRow, found := data.ItemMedicine.GetByMedicineID(medicineID)
  209. if !found {
  210. p.ResponseCode(session, code.ConfigNotFound_Medicine)
  211. return
  212. }
  213. // 检测道具是否足够
  214. errCode := asset.Service().Enough(p.playerID, medicineID, 1)
  215. if code.IsFail(errCode) {
  216. p.ResponseCode(session, errCode)
  217. return
  218. }
  219. curUseNum, exist := itemTable.MedicineMap[medicineID]
  220. if !exist {
  221. curUseNum = 0
  222. }
  223. // 使用次数已达上限
  224. if curUseNum >= medicineRow.DayUseNumLimit {
  225. p.ResponseCode(session, code.ItemUseNumLimit)
  226. return
  227. }
  228. // 调用map使用药品
  229. useReq := &pb.I32I32{
  230. Key: teamID,
  231. Value: medicineID,
  232. }
  233. errCode = p.mapPlayerCallWait(playerTable.Map.MapNodeID, nameRemote.MapPlayer_UseMedicine, useReq)
  234. if code.IsFail(errCode) {
  235. p.ResponseCode(session, errCode)
  236. return
  237. }
  238. // 扣除资源
  239. asset.Service().Sub(p.playerID, medicineID, 1, ao.ItemMedicineUse)
  240. curUseNum++
  241. itemTable.MedicineMap[medicineID] = curUseNum
  242. itemTable.Save2Queue()
  243. resp := &pb.I32I32{
  244. Key: medicineID,
  245. Value: curUseNum,
  246. }
  247. p.Response(session, resp)
  248. }