找回密码
 register
搜索
查看: 13|回复: 0

洱海月 云晓晓魂玉转化断言

[复制链接]
  • 打卡等级:本地老炮
  • 打卡总天数:533
  • 打卡月天数:22
  • 打卡总奖励:530
  • 最近打卡:2026-06-24 01:45:59
Waylee 发表于 2026-4-30 12:30 | 显示全部楼层 |阅读模式 | Google Chrome | Windows 10

马上注册,查看网站隐藏内容!!

您需要 登录 才可以下载或查看,没有账号?register

×

***付费内容***

这篇记录 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'

同时魂玉转化还有两个体验问题:

  1. 格子里有大量魂玉时,旧逻辑可能按整格批量扣,行为不清晰。
  2. 如果改成每次只扣 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 金币 后可完整阅读
支持付费阅读,激励作者创作更好的作品。
您需要登录后才可以回帖 登录 | register

本版积分规则

QQ|雪舞知识库 ( 浙ICP备15015590号-1 | 萌ICP备20232229号|浙公网安备33048102000118号 )|天天打卡

GMT+8, 2026-6-24 05:31 , Processed in 0.062089 second(s), 25 queries .

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表