iThorgrim
Member
LEVEL 3
90 XP
Hello,
Today we're going to take a look at how to create a small script; the famous Mr Spell used on many, if not almost all, UltraFun servers this decade.
This tutorial is aimed at beginner developers wishing to learn how to create a "complex" gossip_menu.
The script is neither optimized nor intended for any use other than the theme of this tutorial.
This tutorial has been translated from French to English by a translator (DeepL). I apologize in advance for any mistakes that may be made, as I've normally made sure that the essence of the tutorial is not altered.
In this tutorial, we will look at concepts such as :
- Using multidimensional arrays.
- The use of loops.
Let's get started!
Mr Spell
The Mr. Spell is an NPC who allows you to learn a spell that is not of your class.
He's widely used on UltraFun servers to ... to ...
- What's the Mr Spell for, Bob?
- It's to make the game more fun.
- Ah.
So it's used a lot on servers that lack imagination and have trouble crapping out content. (Yes, I take sides during my written tutorials, I don't like ultraFun garbage servers).
Anyway ... enough of this nonsense, it's time to get to the heart of the matter.
To start with, we're going to declare the tables that will be useful to us.
Declarations
So, as you can see, I've already put in some information, simply to help you with the rest.
There are still 2 empty tables:
- MrSpell.spellList: to store spells sorted by class.
- MrSpell.spellCost: used to store spell prices.
Here I'm saying that spell 12550 is called 'Mortal Strike' and that this same spell will cost 50 of our currency.
OnHello
Here it's very simple: when our player talks to the NPC, he's asked to display the list of classes and not to display the class that corresponds to the player.
For example, a Warrior can't see the Warrior class and buy a Warrior spell.
If you wish to enable this, simply remove the :
And so Warriors can buy Warrior spells.
I don't really need to explain what's going on in this piece of code ... I've made a "for" loop to browse my table and display information in a Gossip menu.
Now we need to make the GossipSelect, which will contain 3/4 of our code.
OnSelect
We start by looping through the spells in our complete list.
Here we check that our intid is equal to the classId.
This will be used to display spells that only concern the class we've selected in the main menu.
Then we loop the MrSpell.spellList subarray "for".
(Let me explain):
This is the "main" array, a simple array, except that we're asking it to store spells for a certain class.
So in our table, we have an array.
We say that MrSpell.spellList is a multidimensional array.
And since we need to display the spell names, we'll make a "for" loop to display them.
So here we can see that I've added a local variable (pSpell) whose job is to check whether or not we know the spell.
Depending on whether we know the spell or not, the local variable "msg" will store the information "Already learned" or "Not learned".
This will be used to put it next to the spell name, so that the player is aware of whether he has learned the spell or not.
We then simply list the spells in the chosen category, adding the small local variable "msg" to inform the player.
Here, we're going to use the same method as above, except we're going to reverse the technique.
First, we'll list the spells in our category and check whether the intid sent corresponds to a spell in our category.
Then we ask him to list all the spell prices.
Then we'll ask him to check that the spell we've chosen corresponds to a spell that has a price (this will come in handy later on).
I then ask to register the number of copies of item 40752 I own.
Note that you can change the item entry here to the one of your choice.
The NPC checks that I have the right number of points or that I have more points than the price, otherwise I'll get an error message and be sent back to the main menu.
If I have the right number of points, I ask him to check if I have the spell.
And that's the end of it, I tell him that if I don't have the spell then :
- You take away the cost of the spell.
- Teach me the spell.
- Close the menu.
- Send me a message.
Otherwise, it sends me an error message and redirects me to the main menu.
All that's left is to add the Events that will govern my code.
- 197 being your NPC's entry
I've tried to be as clear as possible, so if you have any questions, don't hesitate!
The complete code looks like this :
Today we're going to take a look at how to create a small script; the famous Mr Spell used on many, if not almost all, UltraFun servers this decade.
This tutorial is aimed at beginner developers wishing to learn how to create a "complex" gossip_menu.
The script is neither optimized nor intended for any use other than the theme of this tutorial.
This tutorial has been translated from French to English by a translator (DeepL). I apologize in advance for any mistakes that may be made, as I've normally made sure that the essence of the tutorial is not altered.
In this tutorial, we will look at concepts such as :
- Using multidimensional arrays.
- The use of loops.
Let's get started!
Mr Spell
The Mr. Spell is an NPC who allows you to learn a spell that is not of your class.
He's widely used on UltraFun servers to ... to ...
- What's the Mr Spell for, Bob?
- It's to make the game more fun.
- Ah.
So it's used a lot on servers that lack imagination and have trouble crapping out content. (Yes, I take sides during my written tutorials, I don't like ultraFun garbage servers).
Anyway ... enough of this nonsense, it's time to get to the heart of the matter.
To start with, we're going to declare the tables that will be useful to us.
Declarations
Code:
local MrSpell = {}
MrSpell.Locale = {
[1] = 'Warrioir',
}
MrSpell.spellList = {
[1] = {},
}
MrSpell.spellCost = { }
So, as you can see, I've already put in some information, simply to help you with the rest.
There are still 2 empty tables:
Code:
MrSpell.spellList = {
[1] = {},
}
MrSpell.spellCost = { }
- MrSpell.spellCost: used to store spell prices.
Code:
local MrSpell = {}
MrSpell.Local = {
[1] = 'Warrior',
}
MrSpell.spellList = {
[1] = {
[12550] = 'Mortal strike',
[69] = 'Heroic strike',
},
}
MrSpell.spellCost = {
[12550] = 50,
[69] = 100,
}
OnHello
Code:
function MrSpell.onHello(event, player, object)
local pClass = player:GetClass();
for classId, className in pairs(MrSpell.Locale) do
if (pClass ~= classId) then
player:GossipMenuAddItem(0, className, 0, classId)
end
end
player:GossipSendMenu(1, object);
end
For example, a Warrior can't see the Warrior class and buy a Warrior spell.
If you wish to enable this, simply remove the :
Code:
if (pClass ~= classId) then
...
end
I don't really need to explain what's going on in this piece of code ... I've made a "for" loop to browse my table and display information in a Gossip menu.
Now we need to make the GossipSelect, which will contain 3/4 of our code.
OnSelect
Code:
function MrSpell.onSelect(event, player, object, sender, intid)
for classId, classSubArray in pairs(MrSpell.spellList) do
end
end
Code:
function MrSpell.onSelect(event, player, object, sender, intid)
for classId, classSubArray in pairs(MrSpell.spellList) do
if (intid == classId) then
for spellId, spellName in pairs(classSubArray) do
end
end
end
end
This will be used to display spells that only concern the class we've selected in the main menu.
Then we loop the MrSpell.spellList subarray "for".
(Let me explain):
Code:
MrSpell.spellList = { }
Code:
MrSpell.spellList = {
[1] = {
[12550] = 'Mortal strike',
[69] = 'Heroic strike',
},
};
We say that MrSpell.spellList is a multidimensional array.
And since we need to display the spell names, we'll make a "for" loop to display them.
Code:
function MrSpell.onSelect(event, player, object, sender, intid)
for classId, classSubArray in pairs(MrSpell.spellList) do
if (intid == classId) then
for spellId, spellName in pairs(classSubArray) do
local pSpell = player:HasSpell(spellId);
local msg = '';
if (pSpell) then
msg = '|CFF009D09Learn|r';
else
msg = '|CFF9D1C00Not learn|r';
end
player:GossipMenuAddItem(0, spellName..' '..msg, 0, spellId)
player:GossipSendMenu(1, object);
end
return
end
end
end
Depending on whether we know the spell or not, the local variable "msg" will store the information "Already learned" or "Not learned".
This will be used to put it next to the spell name, so that the player is aware of whether he has learned the spell or not.
We then simply list the spells in the chosen category, adding the small local variable "msg" to inform the player.
Code:
function MrSpell.onSelect(event, player, object, sender, intid)
for classId, classSubArray in pairs(MrSpell.spellList) do
if (intid == classId) then
for spellId, spellName in pairs(classSubArray) do
local pSpell = player:HasSpell(spellId);
local msg = '';
if (pSpell) then
msg = '|CFF009D09Learn|r';
else
msg = '|CFF9D1C00Not learn|r';
end
player:GossipMenuAddItem(0, spellName..' '..msg, 0, spellId)
player:GossipSendMenu(1, object);
end
return
end
for spellId, _ in pairs(classSubArray) do
if (intid == spellId) then
end
end
end
end
First, we'll list the spells in our category and check whether the intid sent corresponds to a spell in our category.
Code:
function MrSpell.onSelect(event, player, object, sender, intid)
for classId, classSubArray in pairs(MrSpell.spellList) do
if (intid == classId) then
for spellId, spellName in pairs(classSubArray) do
local pSpell = player:HasSpell(spellId);
local msg = '';
if (pSpell) then
msg = '|CFF009D09Learn|r';
else
msg = '|CFF9D1C00Not learn|r';
end
player:GossipMenuAddItem(0, spellName..' '..msg, 0, spellId)
player:GossipSendMenu(1, object);
end
return
end
for spellId, _ in pairs(classSubArray) do
if (intid == spellId) then
for spellCostId, Cost in pairs(MrSpell.spellCost) do
if (spellId == spellCostId) then
local pMoney = player:GetItemCount(40752);
end
end
end
end
end
end
Then we ask him to list all the spell prices.
Then we'll ask him to check that the spell we've chosen corresponds to a spell that has a price (this will come in handy later on).
I then ask to register the number of copies of item 40752 I own.
Note that you can change the item entry here to the one of your choice.
Code:
function MrSpell.onSelect(event, player, object, sender, intid)
for classId, classSubArray in pairs(MrSpell.spellList) do
if (intid == classId) then
for spellId, spellName in pairs(classSubArray) do
local pSpell = player:HasSpell(spellId);
local msg = '';
if (pSpell) then
msg = '|CFF009D09Learn|r';
else
msg = '|CFF9D1C00Not learn|r';
end
player:GossipMenuAddItem(0, spellName..' '..msg, 0, spellId)
player:GossipSendMenu(1, object);
end
return
end
for spellId, _ in pairs(classSubArray) do
if (intid == spellId) then
for spellCostId, Cost in pairs(MrSpell.spellCost) do
if (spellId == spellCostId) then
local pMoney = player:GetItemCount(40752);
if (pMoney >= Cost)then
local pSpell = player:HasSpell(spellId);
else
player:SendNotification('You don\'t have enough points to perform this action.');
MrSpell.onHello(event, player, object);
end
end
end
end
end
end
end
If I have the right number of points, I ask him to check if I have the spell.
Code:
function MrSpell.onSelect(event, player, object, sender, intid)
for classId, classSubArray in pairs(MrSpell.spellList) do
if (intid == classId) then
for spellId, spellName in pairs(classSubArray) do
local pSpell = player:HasSpell(spellId);
local msg = '';
if (pSpell) then
msg = '|CFF009D09Learn|r';
else
msg = '|CFF9D1C00Not learn|r';
end
player:GossipMenuAddItem(0, spellName..' '..msg, 0, spellId)
player:GossipSendMenu(1, object);
end
return
end
for spellId, _ in pairs(classSubArray) do
if (intid == spellId) then
for spellCostId, Cost in pairs(MrSpell.spellCost) do
if (spellId == spellCostId) then
local pMoney = player:GetItemCount(40752);
if (pMoney >= Cost)then
local pSpell = player:HasSpell(spellId);
if (not(pSpell)) then
player:RemoveItem(40752, Cost);
player:LearnSpell(spellId);
player:GossipComplete();
player:SendNotification('Thank you for your purchase.');
else
player:SendNotification('You already know this spell.');
MrSpell.onHello(event, player, object);
end
else
player:SendNotification('You don\'t have enough points to perform this action.');
MrSpell.onHello(event, player, object);
end
end
end
end
end
end
end
- You take away the cost of the spell.
- Teach me the spell.
- Close the menu.
- Send me a message.
Otherwise, it sends me an error message and redirects me to the main menu.
All that's left is to add the Events that will govern my code.
Code:
RegisterCreatureGossipEvent(197, 1, MrSpell.onHello)
RegisterCreatureGossipEvent(197, 2, MrSpell.onSelect)
I've tried to be as clear as possible, so if you have any questions, don't hesitate!
The complete code looks like this :
Code:
local MrSpell = {}
MrSpell.Local = {
[1] = 'Warrior',
}
MrSpell.spellList = {
[1] = {
[12550] = 'Mortal strike',
[69] = 'Heroic strike',
},
}
MrSpell.spellCost = {
[12550] = 50,
[69] = 100,
}
function MrSpell.onHello(event, player, object)
local pClass = player:GetClass();
for classId, className in pairs(MrSpell.Locale) do
if (pClass ~= classId) then
player:GossipMenuAddItem(0, className, 0, classId)
end
end
player:GossipSendMenu(1, object);
end
function MrSpell.onSelect(event, player, object, sender, intid)
for classId, classSubArray in pairs(MrSpell.spellList) do
if (intid == classId) then
for spellId, spellName in pairs(classSubArray) do
local pSpell = player:HasSpell(spellId);
local msg = '';
if (pSpell) then
msg = '|CFF009D09Déjà Appris|r';
else
msg = '|CFF9D1C00Non appris|r';
end
player:GossipMenuAddItem(0, spellName..' '..msg, 0, spellId)
player:GossipSendMenu(1, object);
end
return
end
for spellId, _ in pairs(classSubArray) do
if (intid == spellId) then
for spellCostId, Cost in pairs(MrSpell.spellCost) do
if (spellId == spellCostId) then
local pMoney = player:GetItemCount(40752);
if (pMoney >= Cost)then
local pSpell = player:HasSpell(spellId);
if (not(pSpell)) then
player:RemoveItem(40752, Cost);
player:LearnSpell(spellId);
player:GossipComplete();
player:SendNotification('Thank you for your purchase.');
else
player:SendNotification('You already know this spell.');
MrSpell.onHello(event, player, object);
end
else
player:SendNotification('You don\'t have enough points to perform this action.');
MrSpell.onHello(event, player, object);
end
end
end
end
end
end
end
RegisterCreatureGossipEvent(197, 1, MrSpell.onHello)
RegisterCreatureGossipEvent(197, 2, MrSpell.onSelect)
Last edited:
Liked By 1 member :