Sorrow Admin
Posts : 9 Join date : 2011-03-28 Age : 34 Location : Burning Emblem
| Subject: [Coding]Xanes-Coding Npcs start to finish. Fri Apr 22, 2011 6:25 am | |
| Heres his tutorial for Npcs Learning NPCs, From Start To Finish
Hello, and welcome to my attempt to teach everyone NPC scripts better. Regardless of the countless tutorials, and countless Help threads that have been posted and solved, people still seem to have issues with NPCs. Hopefully, with this tutorial, people will learn something. I hope my tutorial teaches you something, even if it's just one thing.
Class 1: Beginning Level
Lesson 1: Useful programs for scripting
There are many programs that can assist with creating codes/scripts. Some allow key features like language changing, so you can have the proper vocabulary, and highlighting of brackets, so you can see where they open and close. It also helps when trying to organize a script. Here are some programs I recommend. NotePad++ PSPad
Lesson 2: Types of NPCs
There are two main types of NPCs. One that deals with status, and one that doesn't. The easiest way to tell them apart is obviously... the status. When creating an NPC, always think to yourself. "Will my NPC be opening more than 2 windows when I talk to it before it's over?" If your answer is yes, you will most likely need to use status. If you answered no, then status probably won't be needed. Here are some examples in the spoiler. - Spoiler:
NPC not requiring status: - Code:
-
function start() { cm.sendOk("I am an NPC without a status."); cm.dispose(); }
Another NPC not requiring status: - Code:
-
function start() { cm.sendOk("Here is another example of an NPC without status."); }
function action(mode, type, selection) { cm.warp(100000000, 0); cm.gainItem(4001126, 1); cm.sendOk("See? Here, I warped you and gave you an item without using status."); cm.dispose(); }
NPC requiring status - Code:
-
var status;
function start() { status = -1; action(1, 0, 0); }
function action(mode, type, selection) { if (mode == 1) { status++; }else{ status--; } if (status == 0) { cm.sendNext("Go ahead and press Next so I can proceed to the next status."); } else if (status == 1) { cm.sendSimple("Would you like to see how I use status again? \r\n #L0# Yes. #l \r\n #L1# No. #l"); } else if (status == 2) { if (selection == 0) { cm.sendOk("Here I am, in another status. As you can see from the script, this window is in status 2."); cm.dispose(); } else if (selection == 1) { cm.sendOk("Well, sucks to be you, don't it? This window is also in status 2 :) "); cm.dispose(); } } }
Lesson 3: Communicating with yourself and other scripters
One of the key things when scripting is being able to understand what exactly you are doing or being able to understand what the author was doing. What better way to understand than to make notes while scripting? Have you ever looked at a script, and seen two // at the end of a line? Those are called comments. There are a few types of comments, and I will brush up on them. - Code:
-
// This is one form of a comment // When using this type of comment, it is typically used for single lines, hence why it is called a Single-Lined Comment.
/** This is a Multi-Lined Comment Anything in between the *s will not be read by whatever program is reading the script. **/
Comments are ignored by programs when being read. This means whatever you put as a comment, is strictly for the person reading the script. They are very useful if you are writing a long script and need to keep track of what you are doing. They also help by allowing you to tell other people what it is you were intending to do, in case they can't understand just by reading the code.
Lesson 4: Learning the Terminology
Let's face it. You can't code an NPC if you have no idea what the proper terminology is. Here are some quick notes.
NPC Color codes/Item pictures/etc
- Spoiler:
- Code:
-
#b = Blue text. #c[itemid]# Shows how many [itemid] the player has in their inventory. #d = Purple text. #e = Bold text. #f[imagelocation]# - Shows an image inside the .wz files. #g = Green text. #h # - Shows the name of the player. #i[itemid]# - Shows a picture of the item. #k = Black text. #l - Selection close. #m[mapid]# - Shows the name of the map. #n = Normal text (removes bold). #o[mobid]# - Shows the name of the mob. #p[npcid]# - Shows the name of the NPC. #q[skillid]# - Shows the name of the skill. #r = Red text. #s[skillid]# - Shows the image of the skill. #t[itemid]# - Shows the name of the item. #v[itemid]# - Shows a picture of the item. #x - Returns "0%" (need more information on this). #z[itemid]# - Shows the name of the item. #B[%]# - Shows a 'progress' bar. #F[imagelocation]# - Shows an image inside the .wz files. #L[number]# Selection open. \r\n - Moves down a line. \r = Return Carriage \n = New Line \t = Tab (4 spaces) \b = Backwards
cm.[Commands] - Code:
-
dispose Ends the conversation with an NPC, allows you to talk to NPCs again. How to use: cm.dispose();
sendNext Shows a conversation window with a 'Next' button. How to use: cm.sendNext("[text]");
sendPrev Shows a conversation window with a 'Prev' (previous) button. How to use: cm.sendPrev("[text]");
sendNextPrev Shows a conversation window with a 'Next' and 'Prev' button (see above). How to use: cm.sendNextPrev("[text]");
sendOk Shows a conversation window with an 'Ok' button. How to use: cm.sendOk("[text]");
sendYesNo Shows a conversation window with a 'Yes' and 'No' button, 'No' ends the conversation unless otherwise stated. How to use: cm.sendYesNo("[text]");
sendAcceptDecline Shows a conversation window with an 'Accept' and 'Decline' button. 'Decline' ends the conversation unless otherwise stated. How to use: cm.sendAcceptDecline("[text]");
sendSimple Shows a conversation window with no buttons. How to use: cm.sendSimple("[text]");
sendStyle Shows a style-select window. How to use: cm.sendStyle("[Text]", [variable]); // You'll need to declare the variable in a Var statement.
warp Warps the player to a map. How to use: cm.warp([mapid], [portal]); // Set [portal] as 0 if you want default.
openShop Opens a shop window. How to use: cm.openShop([shopid]);
haveItem Checks if the player has an item (in their inventories or equipped). How to use: cm.haveItem([itemid]);
gainItem Gives the player an item/takes an item from a player. How to use: cm.gainItem([itemid],[ammount]); // Change [ammount] to -[ammount] to take an item.
changeJob Changes the job of the player. How to use: cm.changeJob([jobid]);
getJob Finds out what job the player has. How to use: cm.getJob();
startQuest Starts a quest. How to use: cm.startQuest([questid]);
completeQuest Finishes a quest. How to use: cm.completeQuest([questid]);
forfeitQuest Forfeits a quest. How to use: cm.forfeitQuest([questid]);
getMeso Finds out how many mesos a player has. How to use: cm.getMeso();
gainMeso Gives a player mesos/takes mesos from a player. How to use: cm.gainMeso([ammount]); // use -[ammount] to take mesos.
gainExp Gives a player exp/takes exp from a player. How to use: cm.gainExp([ammount]); // use -[ammount] to take exp.
getLevel Finds out the level of the player. How to use: cm.getLevel();
teachSkill Teaches a player a skill. How to use: cm.teachSkill([skillid],[skilllevel],[maxskilllevel]);
get[Stat] Finds out the [Stat] of the player. [Stat] being: HP, MP, STR, DEX, INT, LUK. How to use: cm.get[Stat]();
modifyNX Gives/Takes the player nx How to use: cm.gainNX([amount]); Make it negative to make it take away.
Checking for mesos, items, donator, gm, and gender - Code:
-
if (cm.getPlayer().isGM()) { // checks for GM
if (cm.getChar().isDonator() == true) { // checks for donator
if (cm.getJob().equals(net.sf.odinms.client.MapleJob.BOWMAN)) { // checks for Bowman job (list of jobs in below spoiler)
if (cm.getLevel() >= 30) { // checks if level is more than or equal to 30.
if (cm.getChar().getGender() == 0) { // 0 = male, 1 = female if (cm.getPlayer().getGender() == 0) { // 0 = male, 1 = female
if (cm.getMeso() >= amount) { // >= greater than or equal to, <= less than or equal to, == is equal to
if (cm.haveItem(itemid, amount)) { // checks if the player has a certain amount of an item
if (cm.getPlayer().getitemQuantity(itemid)); // gets a count of how much a player has of an item
Job Terms and Ids - Spoiler:
- Code:
-
BEGINNER - 0 WARRIOR - 100 FIGHTER - 110 CRUSADER - 111 HERO - 112 PAGE - 120 WHITEKNIGHT - 121 PALADIN - 122 SPEARMAN - 130 DRAGONKNIGHT - 131 DARKKNIGHT - 132 MAGICIAN - 200 FP_WIZARD - 210 FP_MAGE - 211 FP_ARCHMAGE - 212 IL_WIZARD - 220 IL_MAGE - 221 IL_ARCHMAGE - 222 CLERIC - 230 PRIEST - 231 BISHOP - 232 BOWMAN - 300 HUNTER - 310 RANGER - 311 BOWMASTER - 312 CROSSBOWMAN - 320 SNIPER - 321 CROSSBOWMASTER - 322 THIEF - 400 ASSASSIN - 410 HERMIT - 411 NIGHTLORD - 412 BANDIT - 420 CHIEFBANDIT - 421 SHADOWER - 422 PIRATE - 500 BRAWLER - 510 MARAUDER - 511 BUCCANEER - 512 GUNSLINGER - 520 OUTLAW - 521 CORSAIR - 522 MAPLELEAF_BRIGADIER - 800 GM - 500(v55) / 900(v62+) SUPERGM 510(v55) / 910(v62+) DAWNWARRIOR1 - 1000 DAWNWARRIOR2 - 1010 DAWNWARRIOR3 - 1011 DAWNWARRIOR4 - 1012 BLAZEWIZARD1 - 1100 BLAZEWIZARD2 - 1110 BLAZEWIZARD3 - 1111 BLAZEWIZARD4 - 1112 WINDARCHER1 - 1200 WINDARCHER2 - 1210 WINDARCHER3 - 1211 WINDARCHER4 - 1212 NIGHTWALKER1 - 1300 NIGHTWALKER2 - 1310 NIGHTWALKER3 - 1311 NIGHTWALKER4 - 1312 THUNDERBREAKER1 - 1400 THUNDERBREAKER2 - 1410 THUNDERBREAKER3 - 1411 THUNDERBREAKER4 - 1412 ARAN1 - 2100 ARAN2 - 2110 ARAN3 - 2111 ARAN4 - 2112
Keep in mind that some repacks use a different Terminology than this, so it's best to browse your repack/source folder to get familiar with yours.
Partial Credits: Here for easy copy/pasting and editing.
Class 2: Intermediate Level
Lesson 1: Learning to code an NPC
When creating an NPC, it's always best to have a general idea of what you are doing. A quick way to do this is to open a Notepad file, with little notes of what you want it to do. Example:
- Code:
-
Item Exchanger Window 1 - Shows different options Window 2 - Shows different options after picking from Window 1
Window 1 choices - ores for a maple coin, ores for a maple leaf Window 2 choices - if picked maple coin = 10 ores for 1 coin, 20 ores for 3 coins if picked maple leaf = 5 ores for 1 leaf, 10 ores for 3 leafs
That was just an example, but by making little notes like that, you can think of the best layout beforehand. It also helps having something to look back on to remind you, in case you don't have the time to do your script in one sitting.
Now, before you begin, you need to think of what type of NPC you are going to do. If you need reference, revert to Class 1, Lesson 2. In this example, I will be using an NPC that uses status. Please read the comments (Class 1, Lesson 3) in the script, to understand what I am doing.
- Code:
-
var status;
function start() { // starts the NPC status = -1; // sets the status of the NPC to -1 action(1, 0, 0); // sets the mode to 1, type to 0, and selection to 0 for the NPC } // closes off the start function
function action(mode, type, selection) { // calls what you set above in function start, almost all actions are done here if (mode == 1) { // the mode is set to 1 because of the function start, as shown above status++; // advances the NPC to the next status, in this case, status 0 }else{ // if mode does not equal 1 status--; // does not advance the NPC to the next status. } if (status == 0) { // if mode was 1, status would move from -1 to 0. If status is 0, these actions will happen cm.sendSimple("Hello. I am going to show you how to use #belse#k and #bif#k. Are you ready? \r\n #L0# Yes, I am. #l \r\n #L1# No, I'm not. #l"); // Opens a window with 2 choices (selections) } else if (status == 1) { // NPC advances to next status if a selection is chosen. if (selection == 0) { // selection 0 is #L0#, "Yes, I am." if (cm.haveItem(4001129, 10)) { // checks for an item cm.sendOk("IF you chose selection 0, or in easier terms, the first choice, I will say this."); // IF you have the item, this will show cm.dispose(); }else{ cm.sendOk("I'm sorry, you don't have the item."); // ELSE, you don't have the item. cm.dispose(); } } else if (selection == 1) { // "No, I'm not." cm.sendOk("ELSE IF you chose selection 1, I will say this."); cm.dispose(); } } }
Here's a little something to help you with NPCs as well.
- Code:
-
----------- sendNext(); & sendOk(); ----------- Type = 0 If end chat - mode = -1 If next/ok - mode = 1
----------- sendNextPrev(); ----------- Type = 0 If end chat - mode = -1 If next - mode = 1 if back - mode = 0
----------- sendYesNo(); ----------- Type = 1 If end chat - mode = -1 If yes - mode = 1 If no - mode = 0
----------- sendAcceptDecline(); ----------- Type = 12 If end chat - mode = -1 If accept - mode = 1 If decline - mode = 0
----------- sendGetText(); ----------- Nothing o____o its something special <3
----------- sendGetNumber(); ----------- Type = 3 If end chat - mode = 0 if ok - mode = 1
----------- sendSimple(); ----------- Type = 4 If end chat - mode = 0 if select - mode = 1
Brackets can be a key factor between a working NPC and a non working NPC. Let's take a look at a part of the above NPC. - Code:
-
if (cm.haveItem(4001129, 10)) { // checks for an item cm.sendOk("IF you chose selection 0, or in easier terms, the first choice, I will say this."); // IF you have the item, this will show cm.dispose(); }
If you look at the first line... - Code:
-
if (cm.haveItem(4001129, 10)) {
There is an opening bracket at the end of the line. This means, in an easier understanding, if the condition is true, anything after will happen. So, for that line, if you have 10 of 4001129, the following line will happen. Look at the end of the snippet of code. Notice the other bracket? - Code:
-
} That is a closing bracket. It closes the condition. So, anything between the opening and closing bracket is for that specific condition. The closing bracket will usually be followed by an else, which means if you don't have the item, the actions under else will happen.
Lesson 2: Using Operators
In this lesson, you will learn the different types of Operators and how to use them. There are a few types of Operators. They can help you determine if something is bigger or smaller, and even if it's equal to. Below is a list of Operators you will most likely be using. - Code:
-
Operator Name Type Description ! Not Unary Returns true if the operand to the right evaluates to false. Returns false If the operand to the right is true.
&& Conditional And Binary If the operand on the left returns false, returns false without evaluating the operand on the right.
|| Conditional Or Binary If the operand on the left returns true, returns true without evaluating the operand on the right.
The above Operators are commonly used in between conditions. What is a condition? Things you see quite often in NPC scripts. - Code:
-
if (cm.haveItem(itemid, amount)) { if (cm.getMeso() >= amount) { if (cm.getPlayer().getLevel() >= amount) {
These are all conditions. So, when used in between, you can further specify certain requirements for players. Examples: - Code:
-
if (cm.haveItem(4001129, 50) && cm.getMeso() >= 1000) { // This requires the player to have both 50 Maple Coins and 1000 Mesos if (cm.haveItem(4001129, 50) || cm.getMeso() >= 1000) { // This checks if the player has either 50 Maple coins [b]or[/b] 1000 Mesos if (!cm.haveItem(4001129, 50)) { // This will check to see if they don't have at 50 Maple Coins
Using this is a good way to limit players from saving up loads of an item to cash in at once. Next, we have Relational Operators. These are commonly used inside conditions. Here is a list of a few. - Code:
-
Operator Description == Returns true if the expression on the left evaluates to the same value as the expression on the right.
< Returns true if the expression on the left evaluates to a value that is less than the value of the expression on the right.
<= Returns true if the expression on the left evaluates to a value that is less than or equal to the expression on the right.
> Returns true if the expression on the left evaluates to a value that is greater than the value of the expression on the right.
>= Returns true if the expression on the left evaluates to a value that is greater than or equal to the expression on the right. I've already given you a few examples of these, if you haven't noticed, but let me run a few more by you. - Code:
-
if (cm.getMeso() >= 1000) { // if player has GREATER THAN OR EQUAL TO 1000 Mesos if (cm.getPlayer().getLevel() == 120) { // if player IS level 120 if (cm.getJobById() <= 112) { // if players' job id is LESS THAN OR EQUAL TO 112 (Hero, Crusader, Fighter, Warrior, Beginner) Lesson 3: Learning how to use Variables
As you progress in your learning experiences, you'll begin to notice and wonder why there are things at the top of a script that start with "var". This is called a variable. Variables, in short, are used to "replace" something. They basically take a number, or a sentence, and "replace it" with whatever you name the variable. Typically, you would want the variable to shorten the script, even if by a few characters. Here is an example. - Code:
-
var gl = 4000313;
function start() { cm.sendOk("Hello, do you have any #v"+gl+"#?"); // Calls the variable gl, and the information it is replacing cm.dispose(); } As you can see from the example, the item id 4000313 is being replaced with the variable "gl". Even though it's only a minor difference, I shortend the script by 1 character. Now imagine having hundreds of places on a script where 4000313 was replaced by "gl". It adds up on how much space you save by using variables. That was an example on how to use a variable for a number. Here is one on how to use a variable for a string, or text sentence. - Code:
-
var yes = "Good Job! You have enough Golden Maple Leafs!"; var no = "I'm sorry, you don't have enough Golden Maple Leafs."; var status;
function start() { status = -1; action(1, 0, 0); }
function action(mode, type, selection) { if (mode == 1) { status++; }else{ status--; } if (status == 0) { cm.sendSimple("#L0# I have 10 Golden Maple Leafs #l \r\n #L1# I have 20 Golden Maple Leafs #l"); }else if (status == 1) { if (selection == 0) { if (cm.haveItem(4000313, 10)) { cm.sendOk(yes); // Calls the variable "yes", and displays what you have set for it cm.dispose(); }else{ cm.sendOk(no); // Calls the variable "no", and displays what you have set for it cm.dispose(); } } else if (selection == 1) { if (cm.haveItem(4000313, 20)) { cm.sendOk(yes); // Calls the variable "yes", and displays what you have set for it cm.dispose(); }else{ cm.sendOk(no); // Calls the variable "no", and displays what you have set for it cm.dispose(); } } } } As you can clearly see, using these variables saved me a great deal of space. Instead of using 202 characters by typing in those long lines, I used 111 characters, and that's including the top variables! Using variables also makes it easier to edit scripts in the future.
Class 3: Advanced Level
Lesson 1: Learning Arrays
Now we get to the fun part. Arrays are what all the "pros" stress about. If Arrays can be used, they want to see it. It not only makes the script cleaner, but helps with the overall performance of the script. Arrays can be easily edited, and once you learn how to use them, can help you understand what the creator of the script is doing. Here are some examples of Arra
- Code:
-
item = [4000313, 4001129, 4001126];
function start() { cm.sendSimple("Which would you like? \r\n #L0# Golden Maple Leaf #l \r\n #L1# Maple Coin #l \r\n #L2# Maple Leaf #l"); }
function action(mode, type, selection) { if (mode == 1) { cm.gainItem(item[selection], 1); } cm.dispose(); } As you can see, the item ids are placed in the order they are shown in the sendSimple. The [selection] in the cm.gainItem method, calls the Array to determine which item to give the player. If they chose the second selection, it would call the second number in the Array. By placing the items in an Array, it shortens the script by a lot. Here is what the script would look like without an Array. - Code:
-
function start() { cm.sendSimple("Which would you like? \r\n #L0# Golden Maple Leaf #l \r\n #L1# Maple Coin #l \r\n #L2# Maple Leaf #l"); }
function action(mode, type, selection) { if (mode == 1) { if (selection == 0) cm.gainItem(4000313, 1); else if (selection == 1) cm.gainItem(4001129, 1); else if (selection == 2) cm.gainItem(4001126, 1); } cm.dispose(); }
There is a clear difference between the two in which is longer. One of the most common ways to use Arrays, is with the infamous for loop, which I will explain in a later lesson.
Lesson 2: Learning Multi-Dimentional Arrays and Randoms
In this section, I will teach you about Multi-Dimentional Arrays and Randoms. Now, I'm not an expert at this, so I'll only be able to show you what I know. So what is a Multi-Dimentional Array? A Multi-Dimentional array is an array made up of separate arrays. Basically, you can have more than one array, and simplify them further by making them into a multi-dimentional array. Unfortunately, I only know how to use them with randoms, so that is what I'll be teaching you. First, lets' set up a simple multi-dimentional array NPC. - Code:
-
var status; var item = [[4001129, 4001129], [1082025, 1102023], [4000313, 1002085]]; var rand = Math.floor(Math.random()*100);
function start() { status -1; action(1, 0, 0); }
function action(mode, type, selection) { if (mode == -1) { cm.dispose(); }else{ if (status >= 2 && mode == 0) { cm.sendOk("See you next time!."); cm.dispose(); return; } if (mode == 1) { status++; }else{ status--; } if (status == 0) { cm.sendYesNo("Are you sure you want to take a chance?"); } else if (status == 1) { var rand2; if ((rand >= 1) && (rand <= 50)) { rand2 = Math.floor(Math.random() * item[0].length); } else if ((rand >= 51) && (rand <= 90)) { rand2 = Math.floor(Math.random() * item[1].length); }else{ rand2 = Math.floor(Math.random() * item[2].length); } cm.gainItem([rand >= 1 && rand <= 50 ? item[0][rand2] : rand >= 51 && rand <= 90 ? item[1][rand2] : item[2][rand2]]); cm.sendOk("Congrats on your item."); cm.dispose(); } } } Alright, time to explain. This is the multi-dimentional array.
- Code:
-
var item = [[4001129, 4001129], [1082025, 1102023], [4000313, 1002085]];
Each separete array is colored, so you can see the 3 different arrays within the multi-dimentional array. Next is the random part, or this little snippet. - Code:
-
var rand2; if ((rand >= 1) && (rand <= 50)) { rand2 = Math.floor(Math.random() * item[0].length); } else if ((rand >= 51) && (rand <= 90)) { rand2 = Math.floor(Math.random() * item[1].length); }else{ rand2 = Math.floor(Math.random() * item[2].length); } Ok, at the top of the script, you see this line.
- Code:
-
var rand = Math.floor(Math.random()*100);
Think of this as if it were a dice. The number symbolizes the sides of a dice. So on this dice, there are 100 sides. Continue reading the random part as you read this. If the dice lands on side 1 - 50, give an item in the first array. If the dice lands on side 51 - 90, give an item in the second array. If it lands on any other side, give an item in the third array. This line here...
- Code:
-
cm.gainItem([rand >= 1 && rand <= 50 ? item[0][rand2] : rand >= 51 && rand <= 90 ? item[1][rand2] : item[2][rand2]]); Basically follows through with the action. That is all I know about these types of arrays.
Lesson 3: Learning the Infamous For Loop
Have you ever looked at a script, and seen something like this?
- Code:
-
for (var i = 0; i < options.length; i++) text += "\r\n#L"+i+"#"+options[i]+"#l";
This is the infamous for loop. It simplifies almost anything it is used with. It was created to specifically deal with Arrays. Here is the entire part of the code, so I can explain what it means.
- Code:
-
var text = "#e#k What region have you trained in?#b"; var options = new Array("Aqua Road Region = 1 #v4001010#", "Ariant Region = 1 #v4001011#", "El Nath Region = 1 #v4001013#", "Ludas Lake Region = 1 #v4001012#", "Minar Forest Region = 1 #v4001009#", "Victoria Island Region = 5 #v4001126#", "World Tour = 5 #v4001129#", "Ores/Crystals = 1 #v4001014#"); for (var i = 0; i < options.length; i++) text += "\r\n#L"+i+"#"+options[i]+"#l"; cm.sendSimple(text);
Ok so, i = 0. While 0 is less than the options array, do the code. After executing the code, do i++. i++ basically tells the program running the script to increase i until it reaches the length of the array. So since the options array has a length of 8, it will keep increasing until it displays all 8 options.
| |
|