Skip to content

How to create NPC script

Etorth edited this page Jun 16, 2022 · 8 revisions

NPC script needs a special name format

<map_name>.<npc_name>.lua

an example:

道馆_1.栗子商人_1.lua

here map_name is where the NPC stands, npc_name is the NPC's name, if there are NPC's with same name, use _<index> as in the example, name duplication is common in mir3, like several 六面神石 on same map.

Put the script in <server-dir>/script/npc to get automatically loaded.

NPC script is written in lua, an example for the 栗子商人

local function onSellChestnut(uid, chestnutName, currTagName, lastTagName)
    local priceTable = {
        ['金色栗子'] = 10000,
        ['银色栗子'] =  5000,
        ['铜色栗子'] =  2000,
        ['褐色栗子'] =  1000,
    }

    if priceTable[chestnutName] == nil then
        fatalPrintf('Invalid chestnut name: %s', tostring(chestnutName))
    end

    if uidUseItem(uid, chestnutName, 1) then
        uidPostGold(uid, priceTable[chestnutName])
        uidPostXML(uid,
        [[
            <layout>
                <par>请收下<t color="red">%d</t>金币</par>
                <par></par>
                <par><event id="%s" arg="%s">还卖其它的%s</event></par>
                <par><event id="%s">前一步</event></par>
                <par><event id="%s">关闭</event></par>
            </layout>
        ]], priceTable[chestnutName], currTagName, chestnutName, chestnutName, lastTagName, SYS_NPCDONE)
    else
        uidPostXML(uid,
        [[
            <layout>
                <par>哼,你没带%s来,快去找!</par>
                <par></par>
                <par><event id="%s">关闭</event></par>
            </layout>
        ]], chestnutName, SYS_NPCDONE)
    end
end

setEventHandler(
{
    [SYS_NPCINIT] = function(uid, value)
        if uidQueryRedName(uid) then
            uidPostXML(uid,
            [[
                <layout>
                    <par>跟你这种人我无话可说。</par>
                    <par></par>
                    <par><event id="%s">关闭</event></par>
                </layout>
            ]], SYS_NPCDONE)
        else
            uidPostXML(uid,
            [[
                <layout>
                    <par>欢迎光临,对,我就是买栗子的。 如果你能给我找来那些味道又好,营养又好的栗子,我就送你一份大礼。</par>
                    <par>你有栗子吗?</par>
                    <par></par>
                    <par><event id="npc_goto_trade" arg="金色栗子">带来了金色栗子,你要卖吗?</event></par>
                    <par><event id="npc_goto_trade" arg="银色栗子">带来了银色栗子,你要卖吗?</event></par>
                    <par><event id="npc_goto_trade" arg="铜色栗子">带来了铜色栗子,你要卖吗?</event></par>
                    <par><event id="npc_goto_trade" arg="褐色栗子">带来了褐色栗子,你要卖吗?</event></par>
                    <par><event id="%s">马上去给你找</event></par>
                </layout>
            ]], SYS_NPCDONE)
        end
    end,

    ["npc_goto_trade"] = function(uid, value)
        onSellChestnut(uid, value, "npc_goto_trade", SYS_NPCINIT)
    end,
})

Another example to support daily quest, quests are modeled by FSM, take 铁匠 as example:

local events = rotable({
    {name = 'event::接受任务'     , from = 'state::可接受任务'        , to = 'state::已接受任务'        },
    {name = 'event::集齐所需矿石' , from = 'state::已接受任务'        , to = 'state::已经收集到所需矿石'},
    {name = 'event::把矿石交给NPC', from = 'state::已经收集到所需矿石', to = 'state::任务完成'          },
})

local callbacks = rotable({
    ['on_enter_state::' ... '接受任务'] = function(uid)
        uidExecute(uid, [[
            addTrigger(ON_ADDITEM, function(item)
                if hasInventoryItem('黑铁矿', 5) then
                    fsm.trigger('event::集齐所需矿石')
                    return true
                end
                return false
            end)
        ]])
    end

    ['on_enter_state::' ... 'state::已经集齐需要的矿石'] = function(uid)
        uidExecute(uid, [[
            postString('已经收集齐了所需黑铁矿,把它们交给王铁匠把!')
        ]])
    end

    ['on_enter_state::' ... 'state::任务完成'] = function(uid)
        uidExecute(uid, [[
            addDailyQuestDoneCount()
        ]])
    end
})

local fsm = require('statemachine').create(events)
setEventHandler(rotable
{
    [SYS_NPCINIT] = function(uid, value)
        local dailyQuestString <const> = (function()
            if uidQueryDailyQuestNPCName() == getNPCFullName() then
                return '<par><event id = "npc_goto_daily_quest_initial">对今日任务进行了解</event></par>'
            else
                return ''
            end
        end())

        uidPostXML(uid,
        [[
            <layout>
                <par>客官%s你好我是%s,你需要什么帮助?<emoji id="0"/></par>
                <par></par>
                <par><event id="npc_goto_common_service">日常服务</event></par>
                %s
                <par></par>
                <par><event id="%s">关闭</event></par>
            </layout>
        ]], uidQueryName(uid), getNPCName(), dailyQuestString, SYS_NPCDONE)
    end,

    ['npc_goto_common_service'] = function(uid, value)
        uidPostXML(uid,
        [[
            <layout>
                <par>我不会。。。</par>
                <par><event id="%s">关闭</event></par>
            </layout>
        ]], SYS_NPCDONE)
    end,

    ['npc_goto_daily_quest_initial'] = function(uid, value)

        uidPostXML(uid,
        [[
            <layout>
                <par>我需要5个纯度15以上的黑铁矿,你能帮我找到吗?我会有丰厚的酬谢!</par>
                <par></par>
                <par><event id="npc_goto_daily_quest_accept">好的我这就去帮你找</event></par>
                <par><event id="npc_goto_daily_quest_refuse">我再考虑考虑</event></par>
                <par></par>
                <par><event id="%s">关闭</event></par>
            </layout>
        ]], SYS_NPCDONE)
    end

    ['npc_goto_daily_quest_accept'] = function(uid, value)
        fms:trigger('接受任务')
        uidPostXML(uid,
        [[
            <layout>
                <par>那就谢谢你了!</par>
                <par></par>
                <par><event id="%s">关闭</event></par>
            </layout>
        ]], SYS_NPCDONE)
    end
})