• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

[Research] New ways of bulding Npcs

Could you post an example of an NPC you have had in use? Would be nice to see in a educational purpose.
 
Could you post an example of an NPC you have had in use? Would be nice to see in a educational purpose.
The hairycles dynamic trade list and greet:
Lua:
local function getTable(cid)
    local player = Player(cid)
    
    local itemsList = {
        {name="Banana", id=2676, buy=2},
    }

    local statues = {
        {name="Monkey Statue (No Seeing)", id=5086, buy=65},
        {name="Monkey Statue (No Hearing)", id=5087, buy=65},
        {name="Monkey Statue (No Speaking)", id=5088, buy=65}
    }

    if player:getStorageValue(Storage.TheApeCity.Questline) >= 23 then
        for i = 1, #statues do
            table.insert(itemsList, statues[i])
        end
    end
    
    return itemsList
end

local function onTradeRequest(cid)
    return (Player(cid):getStorageValue(Storage.TheApeCity.Mission06) >= 3)
end

local function greetCallback(cid)
    local player = Player(cid)
    if player:getStorageValue(Storage.TheApeCity.Questline) <= 14 then
        npcHandler:setMessage(MESSAGE_GREET, "Oh! Hello! Hello! Did not notice!")
    elseif player:getStorageValue(Storage.TheApeCity.Questline) >= 15 then
        npcHandler:setMessage(MESSAGE_GREET, "Be greeted, friend of the ape people. If you want to trade, just ask for my offers. If you are injured, ask for healing.")
    end
    
    return true
end

npc:setCallback(CALLBACK_GETTRADEITEMLIST, getTable)
npc:setCallback(CALLBACK_ONTRADEREQUEST, onTradeRequest)
npc:setCallback(CALLBACK_GREET, greetCallback)

Now thinking about it, the greetCallback could return the greet message instead of editing the NPC message every time someone greet him.
 
seeing the chondur example I can only think this design could be improved further more. It looks like modal windows, which become hard to deal when you have a deep topic.
Imagine conversations as decision trees, this make it very easier to spot a way to identify them without using many tables...
 

Attachments

If there's interest in a new system I can see about open-sourcing OX's NPC system.
It looks like it's what inspired this thread.

Example NPC:
Lua:
local npc = NPC({
	name = "King Tibianus",
	city = "Thais",
	look = {
		id = 332,
		head = 0,
		body = 0,
		legs = 0,
		feet = 0,
		addons = 0
	},
	phrases = {
		greetings = {
			{'hail', 'king'},
			{'hello', 'king'},
			{'salutations', 'king'},
		}
	},
	messages = {
		greet = "I greet thee, my loyal {subject} |PLAYERNAME|.",
		farewell = "Good bye, |PLAYERNAME|!",
		walkaway = "How rude!"
	},
	promotion = 20000,
	roles = {
		"promotion",
	},
	quests = {
		"the isle of evil quest",
		"the new frontier quest"
	},
	actor = "King Tibianus"
})

function onThink()
	npc:onThink()
end

function onCreatureSay(creature, type, msg)
	npc:onSpeak(creature, type, msg)
end

function onCreatureDisappear(creature)
	npc:onDisappear(creature)
end

function onPlayerCloseChannel(creature)
	npc:onCloseChannel(creature)
end

Example role:
Lua:
local plugin = {}
plugin.name = "Promotion"
plugin.events = {}

function plugin.events.onPromotion(state, event, from, to)
	local player = state.player
	local npc = state.npc
	local msg = state.lastMessage

	if from == 'none' then
		npc:talk("Do you want to be promoted in your vocation for 20000 gold?", player)
		return true
	elseif from == 'confirm' then
		if not msg:contains("yes") then
			npc:talk("Ok, then not.", player)
			return true
		end

		if player:getLevel() < 20 then
			npc:talk("You need to be at least level 20 in order to be promoted.", player)
			return true
		end

		local promotion = player:getVocation():getPromotion()
		if not promotion then
			npc:talk("You are already promoted.", player)
			return true
		end

		if not player:removeMoney(20000) then
			npc:talk("You do not have enough money.", player)
			return true
		end

		player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE)
		npc:talk("Congratulations! You are now promoted. Visit the sage Eremo for new spells.", player)
		player:setVocation(promotion)
		return true
	end
	return false
end

plugin.states = {
	{name = 'promotion', from = 'none', 	to = 'confirm'},
	{name = 'promotion', from = 'confirm', 	to = 'none'},
}

return plugin
 
If there's interest in a new system I can see about open-sourcing OX's NPC system.
It looks like it's what inspired this thread.

Example NPC:
Lua:
local npc = NPC({
    name = "King Tibianus",
    city = "Thais",
    look = {
        id = 332,
        head = 0,
        body = 0,
        legs = 0,
        feet = 0,
        addons = 0
    },
    phrases = {
        greetings = {
            {'hail', 'king'},
            {'hello', 'king'},
            {'salutations', 'king'},
        }
    },
    messages = {
        greet = "I greet thee, my loyal {subject} |PLAYERNAME|.",
        farewell = "Good bye, |PLAYERNAME|!",
        walkaway = "How rude!"
    },
    promotion = 20000,
    roles = {
        "promotion",
    },
    quests = {
        "the isle of evil quest",
        "the new frontier quest"
    },
    actor = "King Tibianus"
})

function onThink()
    npc:onThink()
end

function onCreatureSay(creature, type, msg)
    npc:onSpeak(creature, type, msg)
end

function onCreatureDisappear(creature)
    npc:onDisappear(creature)
end

function onPlayerCloseChannel(creature)
    npc:onCloseChannel(creature)
end

Example role:
Lua:
local plugin = {}
plugin.name = "Promotion"
plugin.events = {}

function plugin.events.onPromotion(state, event, from, to)
    local player = state.player
    local npc = state.npc
    local msg = state.lastMessage

    if from == 'none' then
        npc:talk("Do you want to be promoted in your vocation for 20000 gold?", player)
        return true
    elseif from == 'confirm' then
        if not msg:contains("yes") then
            npc:talk("Ok, then not.", player)
            return true
        end

        if player:getLevel() < 20 then
            npc:talk("You need to be at least level 20 in order to be promoted.", player)
            return true
        end

        local promotion = player:getVocation():getPromotion()
        if not promotion then
            npc:talk("You are already promoted.", player)
            return true
        end

        if not player:removeMoney(20000) then
            npc:talk("You do not have enough money.", player)
            return true
        end

        player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE)
        npc:talk("Congratulations! You are now promoted. Visit the sage Eremo for new spells.", player)
        player:setVocation(promotion)
        return true
    end
    return false
end

plugin.states = {
    {name = 'promotion', from = 'none',     to = 'confirm'},
    {name = 'promotion', from = 'confirm',     to = 'none'},
}

return plugin
thanks for sharing but apparently there are some design flaws in your example as well. If I'm already promoted why the npc would ask if I want to get promoted and wait for me to confirm before saying I can't do that? Imagine for longer and deeper dialogs, will I have to treat each case? Also this states you created are very confusing and easy to lost the flow.
 
thanks for sharing but apparently there are some design flaws in your example as well. If I'm already promoted why the npc would ask if I want to get promoted and wait for me to confirm before saying I can't do that? Imagine for longer and deeper dialogs, will I have to treat each case? Also this states you created are very confusing and easy to lost the flow.

There are conditional checks in the state system you can add, but that's not how real tibia works for this specific NPC iirc.
The system is versatile enough to handle 900+ NPCs from real tibia, it should good enough for every scenario, but alright I'll let you take over.
 
Last edited:
real tibia also sucks. Cipsoft is just a regular company with very below-average developers. Also the game is a huge legacy project dated over 20 years? Everytime I see a 201X developer trying to use something like the leaked files "because it seems better" I die a little on the inside.

Your design was not friendly made, you may also not see it because you've spent many hours adapting to it. Dealing with "from" and "to" is unnecessary since you could just use numbers in the events and use the order in one single parameter as showed in [Lua] local npc = new NPC("Chondur") -- Loads default, passes name ---- internally, - Pastebin.com (https://pastebin.com/WHjdCY8v). This would also make it easier to adapt the conditions for multiple events using isInArray.

In my honest opinion, the sooner we start to move TFS to the next level (drop the anchor that is this dumb compatibility), the best it will be for this community. We need new designs, new architectures and new thinking. Explore more scripting languages and making changes way more dynamical than it is now. This would also make TFS WAAAAAY more stable as scripts usually don't crash the server, specially with well treated.
 
real tibia also sucks. Cipsoft is just a regular company with very below-average developers. Also the game is a huge legacy project dated over 20 years? Everytime I see a 201X developer trying to use something like the leaked files "because it seems better" I die a little on the inside.

Your design was not friendly made, you may also not see it because you've spent many hours adapting to it. Dealing with "from" and "to" is unnecessary since you could just use numbers in the events and use the order in one single parameter as showed in [Lua] local npc = new NPC("Chondur") -- Loads default, passes name ---- internally, - Pastebin.com (https://pastebin.com/WHjdCY8v). This would also make it easier to adapt the conditions for multiple events using isInArray.

In my honest opinion, the sooner we start to move TFS to the next level (drop the anchor that is this dumb compatibility), the best it will be for this community. We need new designs, new architectures and new thinking. Explore more scripting languages and making changes way more dynamical than it is now. This would also make TFS WAAAAAY more stable as scripts usually don't crash the server, specially with well treated.

I didn't write this example for this thread, I simply pasted it from our REAL TIBIA SERVER.
You're in a forum dedicated to Open Tibia, based off of CipSoft's game, so I would be a bit more respectful.
You would be better of going to a forum dedicated to general game development if you don't like Tibia.
Again, the system does support what you were asking, but you seem to know what you're doing, so I'll let you develop whatever it is you're trying to do. Good luck.
 
If there's interest in a new system I can see about open-sourcing OX's NPC system.
It looks like it's what inspired this thread.

Example NPC:
Lua:
local npc = NPC({
    name = "King Tibianus",
    city = "Thais",
    look = {
        id = 332,
        head = 0,
        body = 0,
        legs = 0,
        feet = 0,
        addons = 0
    },
    phrases = {
        greetings = {
            {'hail', 'king'},
            {'hello', 'king'},
            {'salutations', 'king'},
        }
    },
    messages = {
        greet = "I greet thee, my loyal {subject} |PLAYERNAME|.",
        farewell = "Good bye, |PLAYERNAME|!",
        walkaway = "How rude!"
    },
    promotion = 20000,
    roles = {
        "promotion",
    },
    quests = {
        "the isle of evil quest",
        "the new frontier quest"
    },
    actor = "King Tibianus"
})

function onThink()
    npc:onThink()
end

function onCreatureSay(creature, type, msg)
    npc:onSpeak(creature, type, msg)
end

function onCreatureDisappear(creature)
    npc:onDisappear(creature)
end

function onPlayerCloseChannel(creature)
    npc:onCloseChannel(creature)
end

Example role:
Lua:
local plugin = {}
plugin.name = "Promotion"
plugin.events = {}

function plugin.events.onPromotion(state, event, from, to)
    local player = state.player
    local npc = state.npc
    local msg = state.lastMessage

    if from == 'none' then
        npc:talk("Do you want to be promoted in your vocation for 20000 gold?", player)
        return true
    elseif from == 'confirm' then
        if not msg:contains("yes") then
            npc:talk("Ok, then not.", player)
            return true
        end

        if player:getLevel() < 20 then
            npc:talk("You need to be at least level 20 in order to be promoted.", player)
            return true
        end

        local promotion = player:getVocation():getPromotion()
        if not promotion then
            npc:talk("You are already promoted.", player)
            return true
        end

        if not player:removeMoney(20000) then
            npc:talk("You do not have enough money.", player)
            return true
        end

        player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE)
        npc:talk("Congratulations! You are now promoted. Visit the sage Eremo for new spells.", player)
        player:setVocation(promotion)
        return true
    end
    return false
end

plugin.states = {
    {name = 'promotion', from = 'none',     to = 'confirm'},
    {name = 'promotion', from = 'confirm',     to = 'none'},
}

return plugin
heyy, thats pretty cool, would you release it? I remember when Jiddo posted the new lua npc system on otfans, that was so cool and I was so young to understand, yours might be 10x times better.
 
I think you have some good points @Night Wolf but I also think you're a bit too ambitious. The way Tibia works generally, is "outdated". Even the gameplay that we love shows clear indications of outdated programming. If you start optimizing everything then where do we draw the line for calling the game Tibia still?
 
heyy, thats pretty cool, would you release it? I remember when Jiddo posted the new lua npc system on otfans, that was so cool and I was so young to understand, yours might be 10x times better.

I don't know @Night Wolf said mine isn't that good, so I'm going to let him develop one.
He's got a great vision for the community.
 
tenor.gif
 
I guess that does not matters but ofc its your system so you decide if its posted or nay, thanks anyways ^^

I just was just fucking around, I'll still try to opensource it, I won't let one asshole spoil it for you guys.
I do need to get permission from some people and clean it up a bit though.
 
@Syntax great that you decided to not let him affect your decisions about contributing once again to the community. However I do not think @Night Wolf really meant any harm toward you personally. He's probably frustrated about the development because he sees more potential in it than most people. I personally think he's overambitious, and acted a bit disrespectful towards you, but I don't think he wants this community any harm. You shouldn't be offended by a persons comment about your work. Your ego is obviously stronger than that.
 
You're in a forum dedicated to Open Tibia, based off of CipSoft's game, so I would be a bit more respectful.

son, this isn't about respect. It's about thinking as a developer. The game was done in a time where computer science didn't even existed. The developers had other things to worry with their limited knowledge and resources. Trying to stick with old stuff just because seems right isn't the way to go, otherwise we would still be using .XML files instead of mysql. But why the advancements are only in code and not in processes? not in design? not in architecture?

I didn't write this example for this thread, I simply pasted it from our REAL TIBIA SERVER.
I understood what you meant with the file from your server but I'm saying even if you shared the whole system and did a PR to the official repo, it would still be as limited as it is today, all you did was put the limitations from XML to Lua.

You would be better of going to a forum dedicated to general game development if you don't like Tibia.
Again, the system does support what you were asking, but you seem to know what you're doing, so I'll let you develop whatever it is you're trying to do. Good luck.
My problem with tibia is that the community is very small and greedy. This make highly technical people like me be frustrated because others usually don't care about concepts like complexity, secure programming and architecture design and those are things you have to use in the very beginning phase of development, otherwise things tend to go very wrong. Also the people who could help in these topics usually only develops for private servers, and they didn't even share their work when the project dies. Again, it's nothing personal with you, you're just like many others here in community. Speaking of that, Isn't OTX that server that was banned for spoofing otserverlist and that was ruined because developers couldn't fix the crashes? oh.

I have had many students in community, some of them are developing their own servers or even using what they have learned to move on to real life projects. This community can't deal with people who feel hurt just because someone suggested an improvement in their work. In any other situation, you would be learning a lot from me.


If you start optimizing everything then where do we draw the line for calling the game Tibia still?
Optimizations have nothing to do with what the game is. Check this analogy:
Lua:
function Sum100(x) return x + 100 end

function Sum100(x)
for i = 1, 100 do x = x + 1 end
return x
end

Doesn't this two codes do the exact same thing? I haven't changed what they are, just how they do it. This is a very simple example but you can extend it to many kinds of optimizations (design, readability, time, memory)
 
Back
Top