最近补帮会城市老脚本时,碰到一个很典型、也很容易让人误判的问题:
玩家点击 NPC 的“帮会资金捐助”,明明帮会现在只有几百金,却总是提示:
帮会资金已经到达上限,不需要再捐助了。
这类问题表面上看像是 city0_building5.lua 写错了,但真正的问题并不在 NPC 脚本本身,而是在 skynet Lua 服务端的帮会兼容层没有把老脚本需要的城市属性补完整。
这篇就只写最终稳定方案,把最终代码和最终思路一次说清楚。
一、先说结论
最终只需要修改一个文件:
\home\ubuntu\Game2\services\guild\guildmanager_core.lua
不用改客户端。
也不用去硬改 city0_building5.lua 的业务逻辑。
最终修法做了三件事:
- 在
Guildmanager 中补齐老脚本需要的 attr = 16
- 让
attr = 16 按当前县衙等级动态返回帮会资金上限
- 统一兼容字符串和数字形式的
guild_id、attr
这样修完以后,老脚本里的:
self:CityGetAttr(selfId, 16)
就能正确拿到:
- 1级县衙
10000000
- 2级县衙
20000000
- 3级县衙
40000000
- 4级县衙
60000000
- 5级县衙
80000000
二、为什么旧脚本会误判
帮会脚本里判断是否允许捐助的代码其实一直没问题:
文件:
\home\ubuntu\Game2\services\scripts\city0_building5.lua
关键逻辑:
local guildmoney = self:CityGetAttr(selfId, ScriptGlobal.GUILD_MONEY)
local guildmaxmoney = self:CityGetAttr(selfId, 16)
if guildmoney < guildmaxmoney then
self:BeginUICommand()
self:UICommand_AddInt(targetId)
self:EndUICommand()
self:DispatchUICommand(selfId, 19822)
else
self:BeginEvent(self.script_id)
self:AddText("#{BPZJ_0801014_003}")
self:EndEvent()
self:DispatchEventList(selfId, targetId)
end
问题在于:
guildmoney 用的是 attr = 7
guildmaxmoney 用的是 attr = 16
而当前 Lua 版服务端原来的 Guildmanager.city_get_attr 只支持:
local eType_GUILD_ATTR = {
[0] = "ind_level",
[1] = "agr_level",
[2] = "com_level",
[3] = "def_level",
[4] = "tech_level",
[5] = "ambi_level",
[7] = "money",
}
也就是说,老脚本真正依赖的 16 根本没实现。
那结果就很简单了:
CityGetAttr(..., 16) 取不到正确值
- NPC 直接误判成“已达上限”
三、最终代码写法
下面是最终落地的写法,直接按这个改即可。
1. 在 guildmanager_core.lua 顶部增加兼容常量和默认资金上限
local eType_GUILD_ATTR = {
[0] = "ind_level", --工业度0
[1] = "agr_level", --农业度1
[2] = "com_level", --商业度2
[3] = "def_level", --防卫度3
[4] = "tech_level", --科技度4
[5] = "ambi_level", --扩张度5
[7] = "money", --帮派资金7
}
local CITY_ATTR_MAX_GUILD_MONEY = 16
local CITY_MAIN_BUILDING_MAX_LEVEL = 5
local DEFAULT_GUILD_CITY_MAX_MONEY = {
[1] = 10000000,
[2] = 20000000,
[3] = 40000000,
[4] = 60000000,
[5] = 80000000,
}
这里的意思很直接:
16 这个老属性,统一定义成“帮会资金上限”
- 如果配置读取失败,也至少有一套老服默认值兜底
2. 增加“读取帮会城市等级”的函数
***付费内容***
这个函数的设计思路是:
- 不从 NPC 脚本里硬编码等级
- 直接去
.Dynamicscenemanager 取当前帮会城的运行时数据
- 优先使用县衙等级
- 如果县衙等级没拿到,再回退城市等级
- 最终把结果限制在
1~5
这一步非常关键,因为帮会资金上限本来就是跟县衙等级绑定的。
3. 增加“按县衙等级返回资金上限”的函数
local function get_guild_city_max_money(guild)
local config_info = configenginer:get_config("config_info") or {}
local guild_cfg = config_info.Guild or config_info.guild or config_info or {}
local level = get_guild_city_level(guild)
local key = string.format("XianYaMaxMoney_%d", math.max(0, level - 1))
local max_money = tonumber(guild_cfg[key])
if max_money ~= nil then
return math.max(0, math.floor(max_money))
end
local fallback = tonumber(guild_cfg.XianYaMaxMoney_0)
if fallback ~= nil then
return math.max(0, math.floor(fallback))
end
return DEFAULT_GUILD_CITY_MAX_MONEY[level] or DEFAULT_GUILD_CITY_MAX_MONEY[1]
end
这里做了三层保障:
- 优先从
ConfigInfo.ini 读取真实配置
- 配置结构异常时,退回
XianYaMaxMoney_0
- 如果配置仍然读不到,再退回内置默认值
这样无论线上配置怎么摆,attr = 16 都不会再返回 0。
4. 修改 get_guild_by_id,兼容字符串 ID
最终写法:
function guildmanager_core:get_guild_by_id(id)
local target_id = tonumber(id)
for _, guild in ipairs(self.guilds) do
local guild_id = tonumber(guild.id)
if (target_id ~= nil and guild_id == target_id) or guild.id == id then
self:ensure_guild_custom_position_names(guild)
return guild
end
end
end
这一步看起来小,实际上非常重要。
因为 skynet 控制台、旧脚本兼容层、部分转发调用里,经常会把:
传成:
如果还是原来那种严格比较,明明帮会存在,也会因为类型不一致查不到,最终继续返回 0。
5. 修改 city_get_attr,正式接入 attr = 16
最终写法:
function guildmanager_core:city_get_attr(guild_id, attr)
guild_id = tonumber(guild_id) or guild_id
attr = tonumber(attr) or attr
local guild = self:get_guild_by_id(guild_id)
if guild == nil then
return 0
end
if attr == CITY_ATTR_MAX_GUILD_MONEY then
return get_guild_city_max_money(guild)
end
local attr_key = eType_GUILD_ATTR[attr]
if attr_key == nil then
return 0
end
if attr_key == "money" then
return get_guild_money_value(guild)
end
return guild[attr_key] or 0
end
这段就是整个修复的核心。
它干了两件事:
- 先把
guild_id 和 attr 统一转成数字
- 如果
attr == 16,就走我们刚刚补的资金上限逻辑
也就是说,从这一刻开始,老脚本里所有:
CityGetAttr(..., 16)
都能拿到正确结果。
6. 顺手把 city_change_attr 也做类型兼容
最终写法:
***付费内容***
这不是本次问题的唯一关键点,但既然已经碰到“字符串参数”这个兼容坑,就顺手把修改接口也补齐,避免后续旧脚本继续踩同一个问题。
四、为什么我不建议直接改 city0_building5.lua
很多人遇到这个问题,第一反应就是在帮会脚本里直接硬写:
local guildmaxmoney = 80000000
这种写法确实能临时过关,但不稳。
原因很简单:
- 帮会城市等级变化后,上限会跟着变
- 别的老脚本以后如果也用
attr = 16,还会继续报错
- 逻辑散落在脚本里,后面维护会越来越乱
所以最稳的修法一定是:
- 保留老脚本原有写法
- 在
Guildmanager 兼容层把老属性补齐
这样做的好处是:
- 一个地方修,所有旧脚本一起生效
- 配置仍然统一走
ConfigInfo.ini
- 后续县衙等级变化也能自动对应正确上限
五、验证方式
改完以后,先做语法检查:
luac -p /home/ubuntu/Game2/services/guild/guildmanager_core.lua
然后进 skynet 控制台,直接验证这几条:
call .Dynamicscenemanager "get_city_runtime_debug_by_guild", 1
call .Guildmanager "city_get_attr", 1, 16
call .Guildmanager "city_get_attr", 1, 7
call .Guildmanager "get_guild_by_id", 1
只要修对了,结果应该满足:
get_city_runtime_debug_by_guild 返回 ok:true
city_get_attr, 1, 16 不再是 0
- 4级县衙通常会回
60000000
- 5级县衙通常会回
80000000
最后再进游戏内测试:
- 点击“帮会资金捐助”
- 当前资金未满时,应该正常打开捐助界面
- 只有资金真的达到上限时,才提示“已达上限”
六、这次修复的价值
这次修复本质上不是在“改一个 NPC 功能”,而是在把老帮会城脚本依赖的一个关键兼容口补完整。
修完之后,带来的收益不只是帮会捐助恢复正常,还有两点额外价值:
- 以后凡是依赖
CityGetAttr(..., 16) 的老脚本,都能直接复用
Guildmanager 对字符串 ID 和数字 ID 的兼容也更稳了
如果你也在把老 C++ 时代的帮会城逻辑往 skynet Lua 里迁,这个补法是值得直接留档的。
因为它不是临时绕过去,而是真正把兼容层补到了该补的位置。
剩余 14% 内容需要支付 66.00
金币 后可完整阅读
支持付费阅读,激励作者创作更好的作品。