课程社群 跟着课程一起练,问题当场问 飞书申请加入课程
5 / 8

传奇3 实战 · lua 实战 传3 版本制作

元宝与货币的区别:绑定、换算与发放怎么写

点这里打开这节课的课程页浮生梦 主讲 · 2025.07.24 · 4 小时 59 分

材料商人的界面上同时出现了元宝和绑定元宝,看着像一种钱的两个数目,其实是两套互不相通的账。这篇把课上的货币这条线整理出来:金条和绑定金条为什么是两个物品、1000 这个换算是写在哪的、绑定元宝买回来的东西为什么不能交易、检测和扣除怎么封成一个函数、发出去的奖励到底该用哪一种。课上有几处判断是把玩家能刷的地方堵住的,这段思路比代码本身更值得记。先看清两套账各走各的,再看这些口子是怎么一处一处堵上的,整条线就顺了。

  1. 元宝和绑定元宝是两套账,能买什么、能卖什么、能不能流通都不同,混用会把绑定洗成流通。
  2. 换算落在物品上:1 根金条 1000 元宝,1 根绑定金条 1000 绑定元宝,比例写在配置里,代码里不再算一遍。
  3. 绑定不是事后打的标记:用绑定元宝买,出来的那一件就是绑定的,不能交易也不能上拍卖行。
  4. 扣除要封成一个函数,先查数量再拿走,数量不够就返回空值,让调用方自己决定提示什么。
  5. 卖出侧的逻辑正好相反,先看背包里的数量,小于 1 就别往下走,直接提示物品不足。
  6. 发出去的东西要看用途:返点可以发元宝,绑定类奖励别用可流通的货币,卖出侧的收益课上定的是五成。

01元宝与绑定元宝:两套账不能混

课上做材料商人的时候,界面上摆着两种钱:元宝和绑定元宝。老师先问了一句「哪个是绑定的」,把物品和货币的关系对了一遍才动手。它们不是同一种钱的两个数字,而是两套各走各的账,能买什么、能不能回流市场都不一样。

换算关系落在物品身上。普通金条一根 1000 元宝,绑定金条一根 1000 绑定元宝,两个物品名字不一样,价格也是分开填的。1000 这个数字写在配置里,代码里不要再算一遍,否则配置改价之后两边就不一致了。课上改价只动配置表,脚本一行没改。

两套账不能互相顶。老师把这句话说得挺直接:如果能用元宝把绑定买来的东西卖出去,那绑定就没意义了,玩家拿绑定元宝买、再换成元宝出手,等于把不能流通的那一份洗成了流通的。买入用哪种货币,卖出给哪种货币,都在动作号里分开写。

换算写在配置里,代码里不再算一遍lua
-- 两个物品、两个价格,各写各的
local 金条价格 = 1000      -- 1 根金条换 1000 元宝
local 绑定金条价格 = 1000  -- 1 根绑定金条换 1000 绑定元宝

-- 用哪种货币、卖出去给哪种货币,交给动作号决定
-- 这里只负责把价格读出来
local price = 取价格(itemName)
说明

把货币名当成参数传,不要写死在逻辑里。课上每次都把「元宝」或者「绑定元宝」作为参数传给同一个函数,换货币只换这个参数,函数体一句不用改。

02绑定是出来的,不是事后打的标记

课上最实在的一段讨论是「绑定到底在哪一步发生」。讨论下来的结论是不用额外写一段把物品设成绑定的代码:买的环节传进去的是绑定元宝,出来的那一件自然就是绑定的,不能交易,也不能挂到拍卖行上。

老师把实物关系讲得很清楚:同一种东西会有两个身份,用元宝买的可以不绑定,用绑定元宝买的直接就是绑定的那一件。他当时是这么定的,只要用绑定元宝购买,系统自动给做绑定,脚本里不需要再补一刀。

这件事在发放上要留心。返利、回收这类要送到玩家背包里的东西,如果发的是绑定元宝,这份资产就不回流市场;发的要是元宝,它就是能流通的。课上给回收额度定的比例是 30%,发出去的是元宝,所以这一段数字看着不大,性质上却是实打实的流通货币。

拍卖行这条要跟着一起想。课上把绑定件挡在拍卖行外面,理由是拍卖行是面向全服流通的口子,绑定件一旦能挂上去,两套账就又通了。买入侧管绑定、卖出侧管货币,两边都要盯着,规则才算真正立住,只堵一头等于没堵。

踩坑记录

课上试过用绑定元宝买回来的东西再用元宝卖出去,这条路是通的,等于把绑定洗成了流通。定规则的时候要盯住卖出侧用哪种货币,光在买入侧限制没有用。

03用哪种货币,交给动作号去定

一个协议要同时管买和卖,还要分清两种货币,靠的是动作号。课上把一到四这四个值排开:一和二走买入方向,一个用元宝、一个用绑定元宝;三和四走卖出方向,一个换回元宝、一个换回绑定元宝。这四个值之外没有别的情况,所以判断写一次就够。

老师这一段拆得很干净:先把动作号翻译成一个货币名,比如「元宝」或者「绑定元宝」,后面拿着这个字符串去查数量、去扣钱。于是检测和扣除只认货币名,不认动作号,逻辑只有一份,加一种货币只需要在翻译那一步加一行。

封包那一层还要挡一道。老师在这层留了个兜底分支,动作号只认这四个值,其他一律走 else,直接封号,顺手记一条日志。他的说法是这就防着有人自己拼包绕过界面,提示归提示,封号归封号,两件事分开写。

两套账的分工
货币对应物品课上的用法
元宝金条买不绑定的东西,卖出去换回元宝
绑定元宝绑定金条买回来就绑定,不能交易、不能上拍卖行
说明

动作号在协议里是数字,到业务里先变成货币名这个字符串再用。课上把这两步分开写,就是为了后面不管买还是卖,扣钱那一段都不用改。

04检测扣除封成一个函数

课上讲到扣钱那一段,老师嫌每次都写两行太啰嗦,原话是「每次写这个太麻烦了」。他把查数量和拿走合成一个函数,外面只管调一次:传玩家、传货币名、传数量。这个名字他起得很直接,叫检测并拿走。

函数里先查够不够,够了才动手拿。老师边写边说自己要传的是哪两个参数,定下来货币名和数量就够了。他也当场测了不足的情况:条件不满足就返回空值,条件满足才真的扣掉。先检测后扣除,这个顺序不能颠倒,先扣再查,查到不足的时候钱已经出去了,还得再补一次反向操作,越补越乱。

返回值要统一。老师用的是空值表示没扣成,调用方拿到空就知道该提示了,不需要再去问一次够不够。这样函数只有一个出口是成功的,判断起来最简单。

读数量的那一步用的是取钱接口,传货币名进去,拿回来的是这个玩家当前这种钱有多少。判断的时候别只比大小,还要防着取回来是空的,所以条件写成「没有值,或者比要扣的数少」,两个情况用或连起来,缺一个都会在某个边界上漏过去。

检测并拿走:够了才扣,不够返回空lua
local function checkTakeMoney(player, moneyName, num)
    -- 先查:够不够
    local have = player:getMoney(moneyName)
    if not have or have < num then
        return nil          -- 不够就返回空,提示交给调用方
    end

    -- 够了再拿走,两个动作别颠倒
    player:setMoney(moneyName, have - num)
    return true
end
说明

货币名走参数,函数体里不要出现「元宝」这两个字。课上换货币的时候只换了传进去的那个字符串,函数一行没动,这样绑定元宝和元宝共用同一段逻辑。

05不够就返回空,提示交给调用方

接上一步:调用方拿到空值之后干什么。课上的写法是拿到空就提示,然后直接返回,不再往下走。老师把这段叫检测命令,他的判断是前端也检测、后端也检测,两边都挡一道,谁也别指望对方。

提示文案要跟货币对上。用元宝买的时候提示元宝不足,用绑定元宝买的时候提示绑定元宝不足,两个文案分开写,玩家才知道该去凑哪一种。提示是给玩家看的,返回空是给代码看的,这两件事别混成一件。

还有一点,扣钱那一步必须放在发物品之前。课上把顺序写死成:检测通过、扣除、再给东西。如果先发东西再扣钱,扣失败的时候东西已经出去了,只能再写一段回收的代码,越补越复杂。

调用方拿到空就直接返回lua
local ok = checkTakeMoney(player, moneyName, price)
if not ok then
    player:提示(moneyName .. "不足")
    return
end

-- 走到这里说明钱已经扣掉了,后面才轮到发物品
发物品(player, itemName, 1)
说明

动作号不在这四个值里的情况,课上是一句提示加一个返回,不给脏数据落库的机会。判断写在最前面,省得后面每一种分支都要再挡一次。

06卖出侧反过来:先看背包够不够

买入侧是先扣钱再发物品,卖出侧正好反着来:先把玩家手里的东西拿走,再把钱给他。课上做卖出的时候,老师第一件事是查背包里的数量,小于一就停下。他当场试了一次,背包里一个都没有,脚本回的是物品不足。

顺序在卖出侧更要紧。先去拿东西再回头查背包,人已经把东西交出去了,这时候再说不足就扯不清。查、扣、给钱三步挨着写,中间别插别的分支,课上就是这么排的。

卖给哪种货币照样由动作号定:三号给元宝,四号给绑定元宝。用绑定元宝买回来的那一件,不允许用元宝卖回去,这条在课上是当红线讲的,因为它是把两套账打通的口子。

卖出:先看背包,再拿走,再给钱lua
-- 卖出侧先查背包,数量不够直接停
local count = 取背包数量(player, itemName)
if not count or count < 1 then
    player:提示("物品不足")
    return
end

拿走物品(player, itemName, 1)
player:setMoney(moneyName, player:getMoney(moneyName) + price)
踩坑记录

卖出侧改动要连着统计一起看。课上把卖出的收益压到一半,买 2000 的东西卖回来只给 1000,同时把卖出从消费统计里摘了出去;只改一处,另一处就会留下刷额度的口子。

07发放与回收:这笔钱该用哪一种

收尾这一段讲发放。课上把要送到玩家背包里的东西分成两类,一类是消费返点,一类是回收额度。回收额度是角色把东西清空之后的一次性补偿,比例课上定的是三成,给出去的是元宝。老师在界面上先把这三个数摆出来才写代码,他说不看清楚这三个数就没法写。

消费那块拆成三个值各记各的:当日消费、累计总购买、用于回收的总额度。老师举的例子很直白,今天买一百、明天买一百,累计就是二百;等玩家真要走、把东西清空的时候,按二百的三成给他六十元宝。这三个值口径不同,别用一个变量糊过去,当日要按天清零,累计要一直留着。

这三个数不是随手记的。当日那个要跟着服务器日期走,跨天得清零,不然昨天的消费会被算到今天头上;累计那个只加不减,物品改价或者下架都不影响它。回收额度是从累计里按比例折出来的,所以它跟累计必须对得上,各算各的迟早会打架。

发放还有一条边界:卖出去的那一笔不算消费。课上把卖出侧从统计里摘出去,收益也压到一半,买 2000 的东西卖回来只给 1000。这么做不是抠,是防着有人边买边卖把消费额度刷上去,把返点套出来。

发放相关的三个值
统计口径用途
当日消费当天零点到二十四点的购买额当日排行榜
累计总购买历史上买过的总额累计名次
回收额度用于回收的那一份总额度按三成折成元宝发放
说明

发放的货币要看用途定:返点、回收这类补偿发元宝,可以流通;要不要发绑定元宝,取决于你希望这份资产回不回流市场。课上给回收发的是元宝,比例三成。

常见问题

元宝和绑定元宝能互相换吗?

课上没有做这两种货币之间的互换。道理在课上讲得很清楚:能换就等于把 绑定元宝 洗成了流通,玩家拿绑定元宝买、再换成元宝出手,绑定规则就白定了。两套账各走各的。

一根金条到底相当于多少元宝?

1000。课上把普通金条定成 1000 元宝,绑定金条定成 1000 绑定元宝,这两个数字写在配置表里,脚本里不再另外算一遍,改价只动配置。

绑定元宝买的东西能不能卖出去?

能卖,但只能换回 绑定元宝。课上把「用绑定元宝买、再用元宝卖」这条路当成漏洞堵掉了,否则绑定和流通之间就有了一道可以走的门,绑定规则等于没写。

检测和扣除为什么要封成一个函数?

课上原话是每次写两行太麻烦,于是把这段封成了 checkTakeMoney。封起来之后顺序被固定成先检测、后扣除,不够就返回空值,调用方拿到空直接提示并返回,写十次也不会哪一次把顺序写反。

前端已经检测过了,后端还要再检测吗?

要。课上的说法是不能相信前端,后端再挡一道。除了正常流程里检测一次,封包那一段还留了一个兜底分支,值不对就直接封号,顺手记一条日志。

卖出为什么不进消费排行榜?

防止边买边卖刷额度。课上把卖出侧从 消费统计 里摘出去,收益也压到一半,买 2000 的东西卖回来只给 1000。两处要一起改,只改一处就留了口子。

关于本文

本文整理自 2025.07.24 的课程妙记「lua 实战 传3 版本制作」。这节课里浮生梦老师做的是材料商人,从两套货币讲起:金条与绑定金条各对应一种钱,用绑定元宝买回来的东西自动绑定,不能交易也不能上拍卖行。他把扣钱这件事封成了一个函数,先检测再拿走,不够就返回空值,还特意把卖出侧的顺序反着走了一遍。最难得的是他讲规矩,哪里可能被玩家刷、哪条路必须堵死,都摊开讲明白了,不是只丢给你一段能跑的代码。正文里的写法取自课上的现场演示,接口名以 996 引擎说明书为准。他讲东西习惯先跑通再讲理,跑不通的那几处也不藏,正好留给我们当排查的样本,这种讲法比只给一段能跑的代码有用得多。

课程原页:飞书妙记 · lua 实战 传3 版本制作

本系列其他篇目