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

洱海月 沙洲冷 NPC 珍兽回收功能思路(非成品)

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

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

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

×

功能目标

在玄武岛增加 NPC“沙洲冷”,玩家与其对话后可以打开珍兽提交框,选择一只珍兽出售。服务端根据珍兽等级发放金币,并删除玩家提交的那只珍兽。

参考配置:

[monster246]
guid=15896887
type=158
name=沙洲冷
title=珍兽收购商
pos_x=84.9041
pos_z=37.9212
dir=14
script_id=112002

参考脚本映射:

112002=\obj\petisland\opetisland_shazhouleng.lua
250000=\event\luoyang\eluoyang_50000.lua

文件清单

需要补齐或确认以下服务端文件:

configs/scene/petisland_monster.ini
services/scripts/scripts.lua
services/scripts/obj/petisland/opetisland_shazhouleng.lua
services/scripts/event/luoyang/eluoyang_50000.lua
lualib/script_base/pet_repair_drop_misc.lua
services/game/packet_parts/social_trade/team_exchange.lua
services/scene/scenecore/mission_challenge.lua

其中:

  • opetisland_shazhouleng.lua 是 NPC 菜单壳。
  • eluoyang_50000.lua 是珍兽回收核心逻辑。
  • pet_repair_drop_misc.lua 提供按珍兽 GUID 查询和删除的脚本接口。
  • team_exchange.luamission_challenge.lua 负责把客户端提交框传来的珍兽 GUID 继续传到脚本层。

注册脚本

先在脚本注册表中加入 NPC 脚本和事件脚本:

[250000] = "scripts.event.luoyang.eluoyang_50000",
[112002] = "scripts.obj.petisland.opetisland_shazhouleng",

注册位置按项目原有结构放入对应的事件脚本区和 NPC 脚本区即可。

NPC 菜单壳

services/scripts/obj/petisland/opetisland_shazhouleng.lua 主要负责:

  1. 显示 NPC 对话。
  2. 枚举“我要出售珍兽”按钮。
  3. 显示“出售的珍兽怎么抓”的帮助说明。
  4. 将提交框回调转发给事件脚本 250000

关键点是 g_eventList 只挂沙洲冷需要的事件:

opetisland_shazhouleng.script_id = 112002
opetisland_shazhouleng.g_eventList = { 250000 }

NPC 默认对话里调用事件脚本的 OnEnumerate

for _, eventId in pairs(self.g_eventList) do
    self:CallScriptFunction(eventId, "OnEnumerate", self, selfId, targetId)
end

当玩家点击“我要出售珍兽”时,NPC 壳调用事件脚本:

self:CallScriptFunction(arg, "OnDefaultEvent", selfId, targetId)

当玩家在提交框中点确定后,NPC 壳要继续转发 OnMissionCheck,并保留珍兽 GUID 参数:

self:CallScriptFunction(
    scriptId,
    "OnMissionCheck",
    selfId,
    targetId,
    scriptId,
    index1,
    index2,
    index3,
    petIndex,
    missionIndex,
    petGuidH,
    petGuidL
)

这里不要只转发 petIndex,否则容易出现客户端选了 B 珍兽、服务端却删除 A 珍兽的问题。

打开提交珍兽框

services/scripts/event/luoyang/eluoyang_50000.lua 负责打开提交框:

function eluoyang_50000:OnDefaultEvent(selfId, targetId)
    self:BeginEvent(self.script_id)
    self:AddText(self.g_MissionName)
    self:AddText(self.g_MissionInfo)
    self:EndEvent()
    self:DispatchMissionDemandInfo(selfId, targetId, self.script_id, self.g_MissionId, 2)
end

本端 Lua5 脚本环境里,DispatchMissionDemandInfo 会读取当前脚本对象的 self.event。因此调用前必须先 BeginEventAddTextEndEvent,不能像部分 Lua4 参考脚本那样直接发送。

done=2 用于打开可提交界面。出售成功后再发一次 done=1,通知客户端提交流程已经完成:

self:DispatchMissionDemandInfo(selfId, targetId, self.script_id, self.g_MissionId, 1)

否则可能出现服务端已经删宠发钱,但客户端仍显示默认失败提示的问题。

出售金额

金额按珍兽等级分段:

function eluoyang_50000:PetValue(petLevel)
    if petLevel == nil or petLevel <= 0 then
        return 0
    end
    if petLevel <= 5 then
        return 225
    end
    if petLevel <= 15 then
        return 595
    end
    if petLevel <= 25 then
        return 1191
    end
    if petLevel <= 35 then
        return 1779
    end
    if petLevel <= 45 then
        return 2450
    end
    if petLevel <= 55 then
        return 3205
    end
    if petLevel <= 65 then
        return 4042
    end
    if petLevel <= 75 then
        return 4964
    end
    if petLevel <= 85 then
        return 5968
    end
    return 7056
end

这套逻辑不限制珍兽来源。只要玩家通过提交框放入珍兽,就按等级结算。

按 GUID 删除珍兽

提交框协议里通常会带:

pet_index
pet_guid_high
pet_guid_low

实现时不要按 pet_index 删除。pet_index 可能受客户端显示顺序、珍兽栏空洞、索引基准影响,导致删错珍兽。更稳的做法是按客户端提交的 GUID 精确查询并删除。

在脚本基础库中补充:

function script_base:LuaFnGetPet_DataIDByGUID(selfId, petGUID_H, petGUID_L)
    local human = self:get_scene():get_obj_by_id(selfId)
    local pet_bag_container = human:get_pet_bag_container()
    local guid = pet_guid_cls.new()
    guid:set(petGUID_H, petGUID_L)
    local pet_detail = pet_bag_container:get_pet_by_guid(guid)
    if pet_detail then
        return pet_detail:get_data_index()
    end
end

function script_base:LuaFnGetPet_LevelByGUID(selfId, petGUID_H, petGUID_L)
    local human = self:get_scene():get_obj_by_id(selfId)
    local pet_bag_container = human:get_pet_bag_container()
    local guid = pet_guid_cls.new()
    guid:set(petGUID_H, petGUID_L)
    local pet_detail = pet_bag_container:get_pet_by_guid(guid)
    if pet_detail then
        return pet_detail:get_level()
    end
end

function script_base:LuaFnDeletePetByGUID(selfId, petGUID_H, petGUID_L)
    local human = self:get_scene():get_obj_by_id(selfId)
    local pet_bag_container = human:get_pet_bag_container()
    local guid = pet_guid_cls.new()
    guid:set(petGUID_H, petGUID_L)
    local index = pet_bag_container:get_index_by_pet_guid(guid)
    if index then
        pet_bag_container:erase_item(index)
        local msg = packet_def.GCRemovePet.new()
        msg.guid = guid
        self:get_scene():send2client(human, msg)
        return true
    end
    return false
end

函数名可按项目已有容器 API 调整,核心原则是:查询、取等级、删除都使用同一个 GUID。

处理提交逻辑

事件脚本 OnMissionCheck 推荐流程:

  1. 没放珍兽时提示玩家。
  2. 没有 GUID 时拒绝处理。
  3. 按 GUID 读取等级和 DataID。
  4. 删除同一个 GUID 对应的珍兽。
  5. 按等级发钱。
  6. 发任务提示,并通知客户端提交完成。

示例:

function eluoyang_50000:OnMissionCheck(
    selfId,
    targetId,
    scriptId,
    index1,
    index2,
    index3,
    indexpet,
    missionIndex,
    petGuidH,
    petGuidL
)
    if indexpet == 255 then
        self:NotifyTip(selfId, "请先放入你要出售的珍兽!")
        return
    end

    if petGuidH == nil or petGuidL == nil or petGuidH == 0 or petGuidL == 0 then
        self:NotifyTip(selfId, "请先放入你要出售的珍兽!")
        return
    end

    local petLevel = self:LuaFnGetPet_LevelByGUID(selfId, petGuidH, petGuidL)
    if petLevel == nil then
        self:NotifyTip(selfId, "请先放入你要出售的珍兽!")
        return
    end

    local dataId = self:LuaFnGetPet_DataIDByGUID(selfId, petGuidH, petGuidL)
    local petName = self:GetSellPetName(dataId)
    local deleted = self:LuaFnDeletePetByGUID(selfId, petGuidH, petGuidL)
    if not deleted then
        self:NotifyTip(selfId, "出售珍兽失败,请重新选择。")
        return
    end

    local moneyNum = self:PetValue(petLevel)
    self:AddMoney(selfId, moneyNum)
    self:NotifyTip(selfId, "成功出售了" .. petName .. ",获得了#{_MONEY" .. tostring(moneyNum) .. "}")

    self:BeginEvent(self.script_id)
    self:AddText(self.g_MissionComplete)
    self:EndEvent()
    self:DispatchMissionDemandInfo(selfId, targetId, self.script_id, self.g_MissionId, 1)
end

如果项目里的删除接口已经稳定返回布尔值,建议按上面这样判断删除结果。若旧接口删除成功但没有返回值,就不要直接用 ret > 0 判断,否则会出现“珍兽已删但提示失败”。

名字兜底

部分服务端的珍兽配置表可能不完整,直接调用 GetPetName(dataId) 可能因为缺配置报错。建议给出售提示单独做兜底:

function eluoyang_50000:GetSellPetName(dataId)
    if dataId == nil or dataId <= 0 then
        return "该珍兽"
    end
    local ok, petName = pcall(self.GetPetName, self, dataId)
    if ok and petName and petName ~= "" then
        return petName
    end
    return "该珍兽"
end

这样即使某个珍兽 DataID 没有名字配置,出售流程也不会中断。

协议层注意点

如果服务端协议层已经解析了 pet_guid_highpet_guid_low,需要确认它们以无符号整数读取,并继续传到场景脚本:

packet.pet_guid_high = stream:readuint()
packet.pet_guid_low = stream:readuint()

场景层转发 OnMissionCheck 时,要注意不要破坏原有参数位置。可以保留 missionIndex 位置,再追加 GUID 参数:

script:OnMissionCheck(
    selfId,
    targetId,
    scriptId,
    index1,
    index2,
    index3,
    petIndex,
    missionIndex,
    petGuidH,
    petGuidL
)

很多 NPC 壳和任务脚本依赖第 8 个参数是 missionIndex,不要为了沙洲冷直接把 GUID 插到中间。

您需要登录后才可以回帖 登录 | register

本版积分规则

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

GMT+8, 2026-6-24 05:56 , Processed in 0.061342 second(s), 26 queries .

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

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