***付费内容***
这篇记录 800128 云晓晓 NPC 的魂玉转化问题。
相关服务端脚本:
services/scripts/obj/suzhou/osuzhou_yunxiaoxiao.lua
一、现象
日志里出现:
scriptenginer:call error =./services/scripts/obj/suzhou/osuzhou_yunxiaoxiao.lua:163: assertion failed!
stack traceback:
[C]: in function 'assert'
./services/scripts/obj/suzhou/osuzhou_yunxiaoxiao.lua:163: in function 'scripts.obj.suzhou.osuzhou_yunxiaoxiao.OnPetSoulPieceZhuanHua'
同时魂玉转化还有两个体验问题:
- 格子里有大量魂玉时,旧逻辑可能按整格批量扣,行为不清晰。
- 如果改成每次只扣 2 个,又会导致两个格子各 100 个时要点 100 次,太麻烦。
最终需求应该是:
魂玉可在同品质间进行转化。
同品质的 2 个魂玉可转化为任一自选的同品质魂玉。
当同时使用绑定和非绑定的魂玉进行转化时,优先消耗绑定魂玉,且转化得到的魂玉为绑定魂玉。
两个格子放入大量魂玉时,应一键按 2:1 批量转换。
二、根因
旧代码里用 assert(TargetItemIndex) 处理玩家输入。
当客户端传来的格子、道具或目标选择不符合预期时,服务端不是提示玩家,而是直接触发 Lua 断言,导致脚本引擎打印 traceback。
旧逻辑还按单个格子的堆叠数做:
Transformd_Count = (Item_Count - Resist) / 2
LuaFnDecItemLayCount(..., 2 * Transformd_Count)
所以格子里有 100 个时,会直接按 100 / 2 = 50 批量转换。这个思路本身可以保留,但需要改成:
- 两个格子合计数量批量转换;
- 总数按
floor((数量1 + 数量2) / 2) 算产物;
- 绑定和非绑定混用时,优先扣绑定;
- 产物绑定状态按“是否混用/是否有绑定材料”处理;
- 不再使用
assert 处理玩家可触发的异常。
三、修复点
1. 文件头增加 require
在 osuzhou_yunxiaoxiao.lua 顶部原有 require 附近增加:
local item_cls = require "item"
示例:
local class = require "class"
local define = require "define"
local script_base = require "script_base"
local osuzhou_yunxiaoxiao = class("osuzhou_yunxiaoxiao", script_base)
local packet_def = require "game.packet"
local item_cls = require "item"
2. 替换 OnPetSoulPieceZhuanHua
直接把原来的 OnPetSoulPieceZhuanHua 函数整体替换成下面这段。
function osuzhou_yunxiaoxiao:OnPetSoulPieceZhuanHua(selfId, targetId, BagPos_1, BagPos_2, TransformTargetIndex)
print("osuzhou_yunxiaoxiao:OnPetSoulPieceZhuanHua", selfId, BagPos_1, BagPos_2, TransformTargetIndex)
if not BagPos_1 or not BagPos_2 or BagPos_2 == BagPos_1 then
self:notify_tips(selfId, "背包位置异常,请重新操作")
return
end
TransformTargetIndex = tonumber(TransformTargetIndex)
if not TransformTargetIndex or TransformTargetIndex < 1 or TransformTargetIndex > 5 then
self:notify_tips(selfId, "选择异常")
return
end
local Item_Index_1 = self:GetBagItemIndex(selfId, BagPos_1)
local Item_Index_2 = self:GetBagItemIndex(selfId, BagPos_2)
if Item_Index_1 == define.INVAILD_ID or Item_Index_2 == define.INVAILD_ID then
self:notify_tips(selfId, "请放入两份魂玉")
return
end
local TransformTarget_1 = TransformTargets[Item_Index_1]
local TransformTarget_2 = TransformTargets[Item_Index_2]
if not TransformTarget_1 or not TransformTarget_2 then
self:notify_tips(selfId, "道具不符。")
return
end
if TransformTarget_1 ~= TransformTarget_2 then
self:notify_tips(selfId, "放入的魂玉品阶不符")
return
end
local TargetItemIndex = TransformTarget_1[TransformTargetIndex]
if not TargetItemIndex then
self:notify_tips(selfId, "选择异常")
return
end
if not self:LuaFnIsItemAvailable(selfId, BagPos_1) or not self:LuaFnIsItemAvailable(selfId, BagPos_2) then
self:notify_tips(selfId, "道具不可用。")
return
end
local Item_Count_1 = self:GetBagItemLayCount(selfId, BagPos_1)
local Item_Count_2 = self:GetBagItemLayCount(selfId, BagPos_2)
if Item_Count_1 < 1 or Item_Count_2 < 1 then
self:notify_tips(selfId, "请放入两份魂玉")
return
end
local Bind_1 = self:GetBagItemIsBind(selfId, BagPos_1)
local Bind_2 = self:GetBagItemIsBind(selfId, BagPos_2)
local Transformd_Count = math.floor((Item_Count_1 + Item_Count_2) / 2)
if Transformd_Count < 1 then
self:notify_tips(selfId, "魂玉数量不足")
return
end
local Need_Dec_Count = Transformd_Count * 2
local Dec_Count_1 = 0
local Dec_Count_2 = 0
if Bind_1 == Bind_2 or Bind_1 then
Dec_Count_1 = math.min(Item_Count_1, Need_Dec_Count)
Dec_Count_2 = Need_Dec_Count - Dec_Count_1
else
Dec_Count_2 = math.min(Item_Count_2, Need_Dec_Count)
Dec_Count_1 = Need_Dec_Count - Dec_Count_2
end
if Dec_Count_1 > Item_Count_1 or Dec_Count_2 > Item_Count_2 then
self:notify_tips(selfId, "魂玉数量不足")
return
end
local Need_Bind = Bind_1 or Bind_2
local function CanReceiveTarget(count)
local obj = self.scene:get_obj_by_id(selfId)
if not obj then
return false
end
local container = obj:get_prop_bag_container()
local temp = item_cls.new()
temp:set_index(TargetItemIndex)
local bag = temp:get_place_bag()
local max_tile_count = temp:get_max_tile_count()
local can_receive_count = 0
local function AddSlotSpace(bag_pos, item)
if not item then
can_receive_count = can_receive_count + max_tile_count
return
end
local item_count = item:get_lay_count()
if bag_pos == BagPos_1 then
item_count = item_count - Dec_Count_1
elseif bag_pos == BagPos_2 then
item_count = item_count - Dec_Count_2
end
if item_count <= 0 then
can_receive_count = can_receive_count + max_tile_count
return
end
if item:get_index() == TargetItemIndex and item:is_bind() == Need_Bind and item:can_lay() then
can_receive_count = can_receive_count + math.max(0, item:get_max_tile_count() - item_count)
end
end
for bag_pos, item in container:ipairs(bag) do
AddSlotSpace(bag_pos, item)
if can_receive_count >= count then
return true
end
end
return false
end
if not CanReceiveTarget(Transformd_Count) then
self:notify_tips(selfId, "背包空间不足")
return
end
if Dec_Count_1 > 0 and not self:LuaFnDecItemLayCount(selfId, BagPos_1, Dec_Count_1) then
return
end
if Dec_Count_2 > 0 and not self:LuaFnDecItemLayCount(selfId, BagPos_2, Dec_Count_2) then
return
end
if self:TryRecieveItemWithCount(selfId, TargetItemIndex, Transformd_Count, Need_Bind) == define.INVAILD_ID then
self:notify_tips(selfId, "背包空间不足")
return
end
self:LuaFnSendSpecificImpactToUnit(selfId, selfId, selfId, 18, 0)
end
四、行为举例
例 1:两个格子各 100 个非绑定
格子1:非绑定 100
格子2:非绑定 100
结果:
扣除 200 个
获得 100 个非绑定目标魂玉
例 2:一个绑定 100,一个非绑定 100
格子1:绑定 100
格子2:非绑定 100
结果:
优先扣绑定 100
再扣非绑定 100
获得 100 个绑定目标魂玉
例 3:一个绑定 1,一个非绑定 100
格子1:绑定 1
格子2:非绑定 100
合计 101 个,只能转换 50 个目标魂玉,需要消耗 100 个原魂玉。
结果:
优先扣绑定 1
再扣非绑定 99
获得 50 个绑定目标魂玉
剩余非绑定 1
例 4:合计奇数
格子1:非绑定 5
格子2:非绑定 4
合计 9 个,只能转换 4 个目标魂玉。
结果:
扣除 8 个
获得 4 个非绑定目标魂玉
剩余 1 个
五、注意点
不要再用 assert 处理玩家可以通过 UI 或封包触发的输入异常。
assert 适合抓开发期内部不变量;这里的格子、道具 ID、目标选择都来自客户端,应该用 notify_tips + return 兜住。
另外,扣材料前一定要先检查目标道具空间。否则一旦先扣材料、后发放失败,就会出现材料丢失。
剩余 0% 内容需要支付 100.00
金币 后可完整阅读
支持付费阅读,激励作者创作更好的作品。