Jump to content
  • 0

Scripting Techniques, Tutorials/Guides


GmOcean

Question


  • Group:  Members
  • Topic Count:  31
  • Topics Per Day:  0.01
  • Content Count:  666
  • Reputation:   93
  • Joined:  04/27/12
  • Last Seen:  

Scripting Techniques, Tutorials and Guides

I'm going to attempt to help everyone here with their scripts while not being directly involved in helping you with them xD.
This will also help future/current scripters get some fresh info and maybe inspire innovative ideas towards scripting.
So that we can prove that us " Scripters " are the superior ragnarok emulation race! Take that Source Code writers!! Jk lol, we need you too biggrin.png.

What I'm going to do, is write detailed tutorials on how to write a few scripts. Starting from the basics to more advanced scripts.
This way everyone can follow along. And hopefully this will help everyone understand how to write a few scripts and even troubleshoot their own scripts.

 

During these guides, I'll be using my own, scripting form, but i'll try not to deviate too far from the norm biggrin.png.

 // A list of <sprite id>s can be found here: Sprite_IDs Credits: Ai4rei

- Beginner Scripts -

Scripts for complete beginners and novice scripters.

Basic Healer & Buffer Script - Complete ( 5 Parts )

A Basic Healer Script. Here we will cover most, if not all of the basics of scripting.

Basic Healer - Your First Script ( Part 1 )


 Okay, for this healer were going to create one that heals you 100%HP&SP when you click on it and choose heal me.
 So let's start off by writing the NPC header.

<map name>,<x>,<y>,<facing>%TAB%script%TAB%<NPC Name>%TAB%<sprite id>,{<code>}
// Facing Directions, each number is the equivalent to a point on a compass.
[1][0][7]
[2] [6]
[3][4][5]

Alright time to begin, starting with the NPC header:

prontera,150,180,4    script    Healer    4_F_ARUNA_POP,{

Now that we have the header out of the way it's time write a little bit of dialogue.
 To do that, were going to use the command: mes "<text>";

mes "[^0000FF Healer ^000000]";
mes "Would you like me to heal you?";

Okay, we got the little bit of dialogue out of the way. Now let's give them the choice of whether or not they want to be healed.

To do so, were going to be using a combination of 2 commands.

First being: if( <condition> ) { <statement> };

and Second being: select("<option>"{,"<option>",...})

*Note - Options can be grouped together, separated by the character ' : '.*

If you are unsure what I mean by " condition " take a look at this: Operators

But we are also going to end the script early, if they decided to choose: ' No thank you. '  by using the command: close;

The reason we are using the " close " command, is because when we used: " mes ", it caused a text window to open up. And we need to " close " that window. Simple right?

if( select("Yes, heal me.:No thank you.") == 2 ){
close;
}

Now that we have that finished, it is time to heal them for 100%HP&SP using the command: percentheal <hp>,<sp>;

And also end the script. Again, using the " close " command.

percentheal 100,100;
close;

Now, all we need to do is add the last } to the end of our script so that it knows there is nothing more to add.
Once your done your script should look something like this:

prontera,150,180,4    script    Healer    4_F_ARUNA_POP,{
mes "[^0000FF Healer ^000000]";
mes "Would you like me to heal you?";
if( select("Yes, heal me.:No thank you.") == 2 ){
close;
}
percentheal 100,100;
close;
}

And that's it for your first script. You just created a healer npc.

Basic Healer with a Zeny Cost - ( Part 2 )

Okay, firstly you should have already completed the, " Basic Healer - Your First Script " guide. If not you need to do so to follow along. Unless of course your proficient enough in scripting you don't need to. In which case you should be heading down to Intermediate Scripts you show-off you!

 

This is going to be a small edit to our script to let player's know it cost zeny to use. Then we are going to check if they have enough zeny. If they do, we'll take it away and heal them.

Let's start by opening up the script we made in " Part 1 ".

prontera,150,180,4    script    Healer    4_F_ARUNA_POP,{
mes "[^0000FF Healer ^000000]";
mes "Would you like me to heal you?";
if( select("Yes, heal me.:No thank you.") == 2 ){
close;
}
percentheal 100,100;
close;
}

Okay, lets add the following line above the menu options:

mes "^FF0000 *Note - It costs 1,000 zeny to get healed.* ^000000";

Next, directly above: percentheal 100,100;   were going to be adding our check to see if they have enough zeny. If they do not, we'll be telling them that they don't have enough zeny. Then we'll end the script.

To do so, we're going to be using the command: if( <condition> ) { <statement> };

If you are unsure what I mean by " condition " take a look at this: Operators

if( Zeny < 1000 ){
mes "I'm sorry, but you don't have enough zeny.";
close;
}

Okay we got the check out of the way. Now we just need to take the zeny away from them if they do have enough zeny.

To do that we'll be setting the constant variable Zeny to a value of Zeny - 1000. To see how this is done look here: Assigning variables

Now then, lets add it in:

Zeny -= 1000;

And that's all there is to edit. When your done it should look something like this:

prontera,150,180,4    script    Healer    4_F_ARUNA_POP,{
mes "[^0000FF Healer ^000000]";
mes "Would you like me to heal you?";
mes "^FF0000 *Note - It costs 1,000 zeny to get healed.* ^000000";
if( select("Yes, heal me.:No thank you.") == 2 ){
close;
}
if( Zeny < 1000 ){
mes "I'm sorry, but you don't have enough zeny.";
close;
}
Zeny -= 1000;
percentheal 100,100;
close;
}

And that's it, you just finished editing your script, and now it costs players 1,000 zeny to get healed.

Basic Healer & Buffer with Zeny cost - ( Part 3 )

Alright, to start this off. I'm assuming you already finished Part 1 & 2. If not, I recommend you do so,
since we will be using those scripts to make this one. I will attempt to continue this pattern until we deviate
away from the healer and move on to a different type of script.

 

So first, let's open up the script we made in ( Part 2 ).

prontera,150,180,4    script    Healer    4_F_ARUNA_POP,{
mes "[^0000FF Healer ^000000]";
mes "Would you like me to heal you?";
mes "^FF0000 *Note - It costs 1,000 zeny to get healed.* ^000000";
if( select("Yes, heal me.:No thank you.") == 2 ){
close;
}
if( Zeny < 1000 ){
mes "I'm sorry, but you don't have enough zeny.";
close;
}
Zeny -= 1000;
percentheal 100,100;
close;
}

Let's start by adding a MES to show how much they cost. For this script were going to make it nice and cheap.
5,000z to get both AGI-UP & BLESSING Lvl 10. Meh, what can I say, " I'm a generous god. " - The movie 300.
And were going to add this right above our menu options.

mes "^FF0000 *Note - AGI-UP + BLESSING Level 10 costs 5,000 zeny.* ^000000";

Now that people know we offer buffs, we need to give them the option to choose to get buffed.
So lets modify our menu to have a 3rd option.

if( select("Yes, heal me.:I want buffs:No thank you.") == 3 ) {
    close;
}

Now that we got that out of the way. It's time to modify our zeny check. It's going to be customized a little bit.
Were now going to use | | and && operators in our checks.
So go ahead and find our zeny check and change it to the below:

if( (Zeny < 1000 && @menu == 1) || (Zeny < 5000 && @menu == 2) ) {
    mes "I'm sorry, but you don't have enough zeny.";
    close;
}

Your probably wondering why were using @menu == 1 or @menu == 2. This is because when ever a menu option is
selected, the variable @menu is set to that option. So, the first option is equal to 1, and the second is equal to 2,
so on and so forth.

 

At anyrate, since we are still in the beginner section of scripting, were going to be adding 1 more check to our script.
So go ahead and add the following to your script:

if( @menu == 1 ) {
    Zeny -= 1000;
    percentheal 100,100;
}

However, this isn't complete. We still need to add the last part, which deals with our buffs.
To do that we're going to be using the command: sc_start <effect type>,<ticks>,<value 1>{,<rate>,<flag>{,<GID>}};
Where, effect type is the buff we want to give them, ticks is how long it will last in 1/1000ths of a second, and value is the level of the buff.
In our case, we are using Blessing Level 10 and AGI-UP Level 10. And to keep things simple, we're going to make it last for 5mins each.
If you wish to add other status-effects then look in this file: const.txt
It will be the things starting with:  SC_ .

So, let's start by finding our previous check and find:  } . And add the following next to it:

else {
    Zeny -= 5000;
    sc_start SC_BLESSING,300000,10;
    sc_start SC_INC_AGI,300000,10;
}

So your probably looking at the 300000 and wondering why it's like that. Well the answer is simple.
Ticks are 1/1,000ths of a second. So to find 5 minutes we do the following math.
1 * 1,000 = 1 second * 60 = 60,000 OR 1 Minute * 5 = 300,000 OR 5 Minutes. So now that we have our number we just put it in.
Okay, now that it's done. Your script should look like the following:

prontera,150,180,4    script    Healer    4_F_ARUNA_POP,{
mes "[^0000FF Healer ^000000]";
mes "Would you like me to heal you?";
mes "^FF0000 *Note - It costs 1,000 zeny to get healed.* ^000000";
mes "^FF0000 *Note - AGI-UP + BLESSING Level 10 costs 5,000 zeny.* ^000000";
if( select("Yes, heal me.:I want buffs:No thank you.") == 3 ) {
    close;
}
if( (Zeny < 1000 && @menu == 1) || (Zeny < 5000 && @menu == 2) ) {
    mes "I'm sorry, but you don't have enough zeny.";
    close;
}
if( @menu == 1 ) {
    Zeny -= 1000;
    percentheal 100,100;
} else {
    Zeny -= 5000;
    sc_start SC_BLESSING,300000,10;
    sc_start SC_INC_AGI,300000,10;
}
Zeny -= 1000;
percentheal 100,100;
close;
}

Now were just going to delete those last 2 lines above:  close;
When your finished deleting them, your script will be done. And it should look something like the below:

prontera,150,180,4    script    Healer    4_F_ARUNA_POP,{
mes "[^0000FF Healer ^000000]";
mes "Would you like me to heal you?";
mes "^FF0000 *Note - It costs 1,000 zeny to get healed.* ^000000";
mes "^FF0000 *Note - AGI-UP + BLESSING Level 10 costs 5,000 zeny.* ^000000";
if( select("Yes, heal me.:I want buffs:No thank you.") == 3 ) {
    close;
}
if( (Zeny < 1000 && @menu == 1) || (Zeny < 5000 && @menu == 2) ) {
    mes "I'm sorry, but you don't have enough zeny.";
    close;
}
if( @menu == 1 ) {
    Zeny -= 1000;
    percentheal 100,100;
} else {
    Zeny -= 5000;
    sc_start SC_BLESSING,300000,10;
    sc_start SC_INC_AGI,300000,10;
}
close;
}

And that's it. You just finished editing your script and now players have the option to receive Increased AGI and Blessing Level 10 at the cost of 5,000 zeny.

Basic Healer & Buffer - Cleaning Up Script ( Part 4 )

Okay, so by now, you've learned how to make a pretty standard healing npc. But, it looks kind of messy. So in this lesson, we're going to clean it up, by getting rid of some of those if( stantements ).

So as always, let's start by opening up the previous script:

prontera,150,180,4    script    Healer    4_F_ARUNA_POP,{
mes "[^0000FF Healer ^000000]";
mes "Would you like me to heal you?";
mes "^FF0000 *Note - It costs 1,000 zeny to get healed.* ^000000";
mes "^FF0000 *Note - AGI-UP + BLESSING Level 10 costs 5,000 zeny.* ^000000";
if( select("Yes, heal me.:I want buffs:No thank you.") == 3 ) {
    close;
}
if( (Zeny < 1000 && @menu == 1) || (Zeny < 5000 && @menu == 2) ) {
    mes "I'm sorry, but you don't have enough zeny.";
    close;
}
if( @menu == 1 ) {
    Zeny -= 1000;
    percentheal 100,100;
} else {
    Zeny -= 5000;
    sc_start SC_BLESSING,300000,10;
    sc_start SC_INC_AGI,300000,10;
}
close;
}

What were going to do here is just delete everything after the last mes command. So highlight everything beneath that and just press DELETE or BACKSPACE ( your choice =P ).

When your done your script should look like this:

prontera,150,180,4    script    Healer    4_F_ARUNA_POP,{
mes "[^0000FF Healer ^000000]";
mes "Would you like me to heal you?";
mes "^FF0000 *Note - It costs 1,000 zeny to get healed.* ^000000";
mes "^FF0000 *Note - AGI-UP + BLESSING Level 10 costs 5,000 zeny.* ^000000";

Looks like we went back to the drawing board. But don't worry, you'll be doing this a lot down the line when writing your own scripts. Finding better ways to write it and such. So, let's start by adding the following line beneath it. This will serve as our new menu:

switch( select("Yes, heal me.", "I want buffs", "No thank you") ) {

I'm sure you've noticed the ' switch ' which took the place of ' if '. We also got rid of the ' == 3 ' part. This is because switch is much cleaner and easier to read. But, first let's explain what it does. Simply, all it does is cycle through labels called:  case 1:   case 2:  etc... depending on the value of a variable or number. In our case, it's the variable @menu which will have either a value of 1, 2 or 3 because of our menu.

If you want a further explanation, you can click the spoiler below for a bit more detail. Otherwise, let's continue to the next step.

switch( select("Yes, heal me.", "I want buffs.", "No thank you.") ) { }
// When you select either of the above, it will assign the variable: @menu to it's corresponding slot in the menu.
// In our case:
// "Yes, heal me." = 1 | "I want buffs." = 2 | "No thank you." = 3

Utilizing the switch() command in combination with our menu we can clean up our scripts to do this:

switch( select("Yes, heal me.", "I want buffs.", "No thank you.") ) {
    case 1:
        mes "You chose option 1, ' Yes, heal me. ' ";
        break;
    case 2:
        mes "You chose option 2, ' I want buffs. ' ";
        break;
    case 3:
        break;
}
close; // <--- This will end the script for all 3 options.

// Additionally the above is the exact same as this:

select("Yes, heal me.", "I want buffs.", "No thank you.");
switch( @menu ) {
    case 1:
        mes "You chose option 1, ' Yes, heal me. ' ";
        break;
    case 2:
        mes "You chose option 2, ' I want buffs. ' ";
        break;
    case 3:
        break;
}
close;

// However, both of those have an unnecessary part. And that part is:

case 3:
    break;

// We can remove it since it does nothing, or we just want the to end the script with close. So we can make it look like this, which will be the final product:

switch( select("Yes, heal me.", "I want buffs.", "No thank you.") ) {
    case 1:
        mes "You chose option 1, ' Yes, heal me. ' ";
        break;
    case 2:
        mes "You chose option 2, ' I want buffs. ' ";
        break;
}
close; // <--- This will end the script for all 3 options.

Okay, let's add the sub-label:  case 1:  to our script. This will deal with players click on " Yes, heal me. ". So let's also add the zeny check & percentheal in there as well. So lets add the following:

case 1:
    if( Zeny < 1000 ) {
        mes "I'm sorry, you don't have enough zeny.";
        close;
    }
    Zeny -= 1000;
    percentheal 100,100;
    break;

That's done. I'm sure you see the command: break; there. What this does is allow us to ' exit ', a pair of { }. Doing so, will allow us to continue on with the script if there is some other things to do. You can take a look at the spoiler above to see how we utilize this. Other wise, let's go a head and make the second label:  case 2: beneath all of that. This one will deal with when they click on " I want buffs. ", so we are going

to be adding the zeny check for that, along with the buffs:

case 2:
    if( Zeny < 5000 ) {
        mes "I'm sorry, but you don't have enough zeny.";
        close;
    }
    Zeny -= 5000;
    sc_start SC_BLESSING,300000,10;
    sc_start SC_INC_AGI,300000,10;
    break;

Now that we got that out of the way. Let's add } to the end of that, to let the script know were done with the: switch() command. Then directly beneath that, were going to add: close; To let the script knows that were ending it. Followed by the final } so that the script knows that there is nothing more to do.

}
close;
}

Once your done with that, your script should look something like the below:

prontera,150,180,4    script    Healer    4_F_ARUNA_POP,{
mes "[^0000FF Healer ^000000]";
mes "Would you like me to heal you?";
mes "^FF0000 *Note - It costs 1,000 zeny to get healed.* ^000000";
mes "^FF0000 *Note - AGI-UP + BLESSING Level 10 costs 5,000 zeny.* ^000000";
switch( select("Yes, heal me.", "I want buffs", "No thank you") ) {
    case 1:
        if( Zeny < 1000 ) {
            mes "I'm sorry, but you don't have enough zeny.";
            close;
        }
        Zeny -= 1000;
        percentheal 100,100;
        break;
    
    case 2:
        if( Zeny < 5000 ) {
            mes "I'm sorry, but you don't have enough zeny.";
            close;
        }
        Zeny -= 5000;
        sc_start SC_BLESSING,300000,10;
        sc_start SC_INC_AGI,300000,10;
        break;
}
close;
}

And there you go, you just finished cleaning up your script. It looks much better, and it still does the exact same thing! Who would of thought biggrin.png

Basic Healer & Buffer - Introduction to variables ( Part 5 )

Alright, so far we've completed Parts 1 -> 4. And we have a working script because of it. Now, what we need to do is, make it easily configurable. Sure, we could scroll through the script, and change the zeny prices. But if we do that, we might make a mistake. So what were going to do is, add a label called:  OnInit: in our script. This label will active each and every time the server starts up, or scripts are reloaded.

And under this label we'll be adding our variables, which will be our focus for when we want to configure things in our script.

For a list of all variable types look here: Variables

So go ahead and open up the script from the last lesson:

prontera,150,180,4    script    Healer    4_F_ARUNA_POP,{
mes "[^0000FF Healer ^000000]";
mes "Would you like me to heal you?";
mes "^FF0000 *Note - It costs 1,000 zeny to get healed.* ^000000";
mes "^FF0000 *Note - AGI-UP + BLESSING Level 10 costs 5,000 zeny.* ^000000";
switch( select("Yes, heal me.", "I want buffs", "No thank you") ) {
    case 1:
        if( Zeny < 1000 ) {
            mes "I'm sorry, but you don't have enough zeny.";
            close;
        }
        Zeny -= 1000;
        percentheal 100,100;
        break;
    
    case 2:
        if( Zeny < 5000 ) {
            mes "I'm sorry, but you don't have enough zeny.";
            close;
        }
        Zeny -= 5000;
        sc_start SC_BLESSING,300000,10;
        sc_start SC_INC_AGI,300000,10;
        break;
}
close;
}

We are going to add the: OnInit: label beneath the final:  close;  command. But let's also add a couple of npc_variables there as well. And let's name those variables:  heal_price   and   buff_price .

Remeber, those are NPC variables. So let's go ahead and add the following:

OnInit:
.heal_price = 1000;
.buff_price = 5000;

Here you can see we used the set command. This is because we are telling the script to set those variables, to those values. In this case, .heal_price to 1,000 and .buff_price to 5,000 since that is what our script uses as prices for healing and buffing. Now, we really should add this command beneath that: end; . Go ahead and click that link to find out why we need to add it.

end;

Alright, we have officially created variables. However as they are now, they aren't doing anything and just taking up space. So now were going to put them to use. Let's comb through our script and find all of our zeny checks. Let's replace all those: 1000  and 5000 with the corresponding variables. When your done, your script should look like this so far:

prontera,150,180,4    script    Healer    4_F_ARUNA_POP,{
mes "[^0000FF Healer ^000000]";
mes "Would you like me to heal you?";
mes "^FF0000 *Note - It costs 1,000 zeny to get healed.* ^000000";
mes "^FF0000 *Note - AGI-UP + BLESSING Level 10 costs 5,000 zeny.* ^000000";
switch( select("Yes, heal me.", "I want buffs", "No thank you") ) {
    case 1:
        if( Zeny < .heal_price ) {
            mes "I'm sorry, but you don't have enough zeny.";
            close;
        }
        Zeny -= .heal_price;
        percentheal 100,100;
        break;
    
    case 2:
        if( Zeny < .buff_price ) {
            mes "I'm sorry, but you don't have enough zeny.";
            close;
        }
        Zeny -= .buff_price;
        sc_start SC_BLESSING,300000,10;
        sc_start SC_INC_AGI,300000,10;
        break;
}
close;

OnInit:
.heal_price = 1000;
.buff_price = 5000;
end;
}

Now, there being put to use! But wait, if you look at the mes commands, they still say 1,000 and 5,000 in them. This isn't good, because if we change the values of our variables, those mes commands won't show the proper amount. And we don't want to type it out EACH and EVERY time we make changes. So let's edit that text. So go over to the line that tells players how much healing costs. And were going to make the following changes:

mes "^FF0000 *Note - It costs "+ .heal_price +" zeny to get healed.* ^000000";

So, as your looking at this, you'll see the following was removed:  1,000  and the following was added in it's place:  "+ .heal_price +" pay close attention to this, as it's very important. Take a close look at these: "+  and  +" . I'm going to go a bit into detail on why we need these in this spoiler. If you already know why we used them, continue on with the lesson.

Firstly, when ever we use the mes command, we need to make a string of text keyword here is:  string. All strings begin with a quotation mark " and ends with a quotation mark ". Everything in between those 2 quotation marks is considered text. And as such will be written out like a sentence. But when we add those "+ and +" we tell the script that we are going to be putting something in the middle of this string. In our case it's the variable .heal_price . The server will then look at this variable and then put down it's data value in stead of its name. Which, we set to 1000. When it's all said and done, this is what a player will see:

*Note - It costs 1000 zeny to get healed.* Not bad huh.

Okay, now let's make the same changes to line where we tell players how much it costs to get buffed, but using the other variable this time:

mes "^FF0000 *Note - AGI-UP + BLESSING Level 10 costs "+ .buff_price +" zeny.* ^000000";

After that we are done! So, your script should look something like this:

prontera,150,180,4    script    Healer    4_F_ARUNA_POP,{
mes "[^0000FF Healer ^000000]";
mes "Would you like me to heal you?";
mes "^FF0000 *Note - It costs "+ .heal_price +" zeny to get healed.* ^000000";
mes "^FF0000 *Note - AGI-UP + BLESSING Level 10 costs "+ .buff_price +" zeny.* ^000000";
switch( select("Yes, heal me.", "I want buffs", "No thank you") ) {
    case 1:
        if( Zeny < .heal_price ) {
            mes "I'm sorry, but you don't have enough zeny.";
            close;
        }
        Zeny -= .heal_price;
        percentheal 100,100;
        break;
    
    case 2:
        if( Zeny < .buff_price ) {
            mes "I'm sorry, but you don't have enough zeny.";
            close;
        }
        Zeny -= .buff_price;
        sc_start SC_BLESSING,300000,10;
        sc_start SC_INC_AGI,300000,10;
        break;
}
close;

OnInit:
.heal_price = 1000;
.buff_price = 5000;
end;
}

Now, every time we want to change how much getting healed or buffed costs, we just change the values of those variables.

 

Basic Item Trader Script - Complete ( 2 Parts )

A basic item trader script. Here we will learn how to give players an item in exchange for other items.

Basic Item Trader Script - Introduction to arrays ( Part 1 )

Alright, since your here, I'm going to assume you completed the previous guide and created a working Healer & Buffer script with a zeny cost. Or, you already know all that.

So let's start off by making our npc header. Don't forget to use those constants for facing direction, and sprite_id. For a list of sprite IDs click the link near the top of my post.

For this npc, I'm going to be using the vending machine sprite and I'm going to name my npc Vending Machine. Your free to choose your own sprite and name.

prontera,152,180,4    script    Vending Machine    2_VENDING_MACHINE1,{

So let's start off making the variables we'll be using for this script. And were going to place those under the label:  OnInit:  again. We are also going to be using npc_variables as well.

The only difference is that this time were going to be using arrays via the command: setarray <array name>[<first value>],<value>{,<value>...<value>};

Now, our script is going to give a player 1 Valkyrie_Helm for 5 different items. So let's add it in:

OnInit:
.reward_item = Valkyrie_Helm;
.reward_amount = 1;
setarray .required_items[0], Red_Potion, White_Potion, Blue_Potion, Jellopy, Poring_Card;
setarray .required_amount[0], 10, 5, 1, 1000, 1;
end;

This tells us, that we're going to be giving a player 1 Valkyrie helm for 10 Red Potions, 5 White Potions, 1 Blue Potions, 1,000 Jellopy and 1 Poring Card. Now that's easy!

Now we need to add in all of our mes commands to give our npc some dialogue. But, since this npc is gonna do a bit of talking lets save it's name in a temporary scope_variable:

.@npc_name$ = "[^FF0000 Vending Machine ^000000]";

Now the variable: .@npc_name$ holds all that information inside of it. And it's definitely a lot easier to type. So let's add the mes command beneath that with our variable:

mes .@npc_name$;

You'll notice I didn't use any quotation marks for that. This is because we used a string variable. And our string variable already has those quotation marks saved inside of it. Now then, let's add in the rest of the mes commands to let player's know how much 1 valkyrie helm costs. To do that were going to be utilizing the command: getitemname(<item id>) and where it asks for <item_id> were going to be using our variables. So let's add the following lines in:

mes "I will give you "+ .reward_amount +" "+ getitemname(.reward_item) +",";
mes "for the following items:";
mes ""+ getitemname(.required_items[0]) +" x"+ .required_amount[0] +"";
mes ""+ getitemname(.required_items[1]) +" x"+ .required_amount[1] +"";
mes ""+ getitemname(.required_items[2]) +" x"+ .required_amount[2] +"";
mes ""+ getitemname(.required_items[3]) +" x"+ .required_amount[3] +"";
mes ""+ getitemname(.required_items[4]) +" x"+ .required_amount[4] +"";

See, that wasn't too hard. It's practically the same thing as the reward variables, just with the extra [#] added. Okay, next let's add in our menu this time were going to keep it to 2 options, so were going to make it that if they choose option 2, end the script with close; since we used mes earlier. BUT there's a catch, we're not going to use the if() command. We're going to use switch() since it's cleaner. We're also going to be adding case 1: followed by a break;

switch( select("Trade in items", "That's a rip off!") ) {
    case 1:
        break;
}
close;

Okay, now that we got that taken care of. We need to add a few checks, the first check being whether or not they have those items. To do that were going to use the command: countitem(<item id>)

Keep in mind that since we have 5 items to check for, we are going to be utilizing the | | operator. Now, this is where things get a little technical, after each | | were going to add the next check on the line beneath.

We're doing this to keep the script nice and neat. Would you rather look at 5 short lines, or 1 really long line that makes you turn your head? So, let's stick to 5 short lines.

Let's add our check directly above the break; we put in earlier. It should look something like this:

if( (countitem(.required_items[0]) < .required_amount[0]) ||
(countitem(.required_items[1]) < .required_amount[1]) ||
(countitem(.required_items[2]) < .required_amount[2]) ||
(countitem(.required_items[3]) < .required_amount[3]) ||
(countitem(.required_items[4]) < .required_amount[4]) ){
    mes "You don't have all the required items.";
    close;
}

Now that we added the check to see if they have all the items, it's time to delete those items, and give them the reward! The commands that let us do that are the following respectively:

delitem <item id>,<amount>{,<account ID>};  and  getitem <item id>,<amount>{,<account ID>};. So let's add that in below our check, and remember to use those variables:

delitem .required_items[0], .required_amount[0];
delitem .required_items[1], .required_amount[1];
delitem .required_items[2], .required_amount[2];
delitem .required_items[3], .required_amount[3];
delitem .required_items[4], .required_amount[4];
getitem .reward_item, .reward_amount;

Okay, now there's only 1 more thing to do, and that's add the last } to the very bottom of our script. After you add that your done. You now have a working item trader script. It should look like the following:

prontera,154,180,4    script    Vending Machine    2_VENDING_MACHINE1,{
.@npc_name$ = "[^FF0000 Vending Machine ^000000]";
mes .@npc_name$;
mes "I will give you "+ .reward_amount +" "+ getitemname(.reward_item) +",";
mes "for the following items:";
mes ""+ getitemname(.required_items[0]) +" x"+ .required_amount[0] +"";
mes ""+ getitemname(.required_items[1]) +" x"+ .required_amount[1] +"";
mes ""+ getitemname(.required_items[2]) +" x"+ .required_amount[2] +"";
mes ""+ getitemname(.required_items[3]) +" x"+ .required_amount[3] +"";
mes ""+ getitemname(.required_items[4]) +" x"+ .required_amount[4] +"";
switch( select("Trade in items", "That's a rip off!") ) {
    case 1:
        if( (countitem(.required_items[0]) < .required_amount[0]) ||
            (countitem(.required_items[1]) < .required_amount[1]) ||
            (countitem(.required_items[2]) < .required_amount[2]) ||
            (countitem(.required_items[3]) < .required_amount[3]) ||
            (countitem(.required_items[4]) < .required_amount[4]) ){
                mes "You don't have all the required items.";
                close;
        }
        delitem .required_items[0], .required_amount[0];
        delitem .required_items[1], .required_amount[1];
        delitem .required_items[2], .required_amount[2];
        delitem .required_items[3], .required_amount[3];
        delitem .required_items[4], .required_amount[4];
        getitem .reward_item, .reward_amount;
        break;
}
close;


OnInit:
.reward_item = Valkyrie_Helm;
.reward_amount = 1;
setarray .required_items[0], Red_Potion, White_Potion, Blue_Potion, Jellopy, Poring_Card;
setarray .required_amount[0], 10, 5, 1, 1000, 1;
end;
}

You just finished making a working item trader npc. But, it's not 100% done yet. We need to do a few more things. We'll deal with those things in the next lesson, for now look at it, and be proud!!

Basic Item Trader Script - Introduction to loops ( Part 2 )

Okay, last lesson I mentioned how the script wasn't 100% done yet, because there were a few things we need to take care of, so let's knock those out. Firstly, let's start by opening the script back up:

prontera,154,180,4    script    Vending Machine    2_VENDING_MACHINE1,{
.@npc_name$ = "[^FF0000 Vending Machine ^000000]";
mes .@npc_name$;
mes "I will give you "+ .reward_amount +" "+ getitemname(.reward_item) +",";
mes "for the following items:";
mes ""+ getitemname(.required_items[0]) +" x"+ .required_amount[0] +"";
mes ""+ getitemname(.required_items[1]) +" x"+ .required_amount[1] +"";
mes ""+ getitemname(.required_items[2]) +" x"+ .required_amount[2] +"";
mes ""+ getitemname(.required_items[3]) +" x"+ .required_amount[3] +"";
mes ""+ getitemname(.required_items[4]) +" x"+ .required_amount[4] +"";
switch( select("Trade in items", "That's a rip off!") ) {
    case 1:
        if( (countitem(.required_items[0]) < .required_amount[0]) ||
            (countitem(.required_items[1]) < .required_amount[1]) ||
            (countitem(.required_items[2]) < .required_amount[2]) ||
            (countitem(.required_items[3]) < .required_amount[3]) ||
            (countitem(.required_items[4]) < .required_amount[4]) ){
                mes "You don't have all the required items.";
                close;
        }
        delitem .required_items[0], .required_amount[0];
        delitem .required_items[1], .required_amount[1];
        delitem .required_items[2], .required_amount[2];
        delitem .required_items[3], .required_amount[3];
        delitem .required_items[4], .required_amount[4];
        getitem .reward_item, .reward_amount;
        break;
}
close;


OnInit:
.reward_item = Valkyrie_Helm;
.reward_amount = 1;
setarray .required_items[0], Red_Potion, White_Potion, Blue_Potion, Jellopy, Poring_Card;
setarray .required_amount[0], 10, 5, 1, 1000, 1;
end;
}

Alright, now that we opened this up, we need to add a check to make sure the player has enough inventory space, and free-weight to carry the reward.

To do so we need to use the command: checkweight(<item id>,<amount>{,<item id>,<amount>,<item id>,<amount>,...}); Only thing is, we're going to check to see if the command fails using this operator: !

So let's go ahead and add that above the first delitem command:

if( !checkweight(.reward_item, .reward_amount) ){

This tells us that, if true ( if the command fails ) to do something. In our case, we want to just tell the player they don't have enough freespace/weight to receive the item. Then were going to add a close; command to end the script early. And lastly close the check with }, so let's go ahead and do that, it should look like this when it's done:

    mes "You don't have enough free inventory space or weight to receive this item.";
    close;
}

Okay, now the script is finished, and you technically don't need to do anything else to it. But, we're here to learn! So we're going to clean up the script a bit using a loop.

The loop we will be using is called: while (<condition>) <statement>; There are other loops that can get the job done, but I feel like this one is best suited since it's simple. Alright, so go ahead and find the following and delete it.

mes ""+ getitemname(.required_items[0]) +" x"+ .required_amount[0] +"";
mes ""+ getitemname(.required_items[1]) +" x"+ .required_amount[1] +"";
mes ""+ getitemname(.required_items[2]) +" x"+ .required_amount[2] +"";
mes ""+ getitemname(.required_items[3]) +" x"+ .required_amount[3] +"";
mes ""+ getitemname(.required_items[4]) +" x"+ .required_amount[4] +"";

Okay, now where those mes commands used to be were going to add our loop. Were going to be using a scope_variable ' .@ ' and were going to compare it to a value we obtain with this command: getarraysize(<array name>). So go ahead and add the following:

while( .@i < getarraysize(.required_items) ){

Just a note, .@i can be any variable name. It doesn't even need to be a scope_variable, but since we only need the variable for this 1 loop, we use a scope so its memory isn't saved once the script ends. However, .@i is most commonly used in cases like these. Now then, let's continue. Beneath that were going to be adding 1 mes command followed by setting the variable .@i + 1. On the line after that we're going to close the loop with }, always need to make sure too close a command with a right-curly bracket when ever a left-curly bracket is used.

Now, the mes command we're going to use is VERY similar to the ones we deleted. The only difference is, instead of using [0] or [1] or [2], we'll be replacing that with [.@i] as such:

    mes ""+ getitemname(.required_items[.@i]) +" x"+ .required_amount[.@i] +"";
    .@i++;
}

Okay since that's done were about 80% done making this script nice and clean. Your script should look something like this:

prontera,154,180,4    script    Vending Machine    2_VENDING_MACHINE1,{
.@npc_name$ = "[^FF0000 Vending Machine ^000000]";
mes .@npc_name$;
mes "I will give you "+ .reward_amount +" "+ getitemname(.reward_item) +",";
mes "for the following items:";
while( .@i < getarraysize(.required_items) ){
    mes ""+ getitemname(.required_items[.@i]) +" x"+ .required_amount[.@i] +"";
    .@i++;
}
switch( select("Trade in items", "That's a rip off!") ) {
    case 1:
        if( (countitem(.required_items[0]) < .required_amount[0]) ||
            (countitem(.required_items[1]) < .required_amount[1]) ||
            (countitem(.required_items[2]) < .required_amount[2]) ||
            (countitem(.required_items[3]) < .required_amount[3]) ||
            (countitem(.required_items[4]) < .required_amount[4]) ){
                mes "You don't have all the required items.";
                close;
        }
        if( !checkweight(.reward_item, .reward_amount) ){
            mes "You don't have enough free inventory space or weight to receive this item.";
            close;
        }
        delitem .required_items[0], .required_amount[0];
        delitem .required_items[1], .required_amount[1];
        delitem .required_items[2], .required_amount[2];
        delitem .required_items[3], .required_amount[3];
        delitem .required_items[4], .required_amount[4];
        getitem .reward_item, .reward_amount;
        break;
}
close;


OnInit:
.reward_item = Valkyrie_Helm;
.reward_amount = 1;
setarray .required_items[0], Red_Potion, White_Potion, Blue_Potion, Jellopy, Poring_Card;
setarray .required_amount[0], 10, 5, 1, 1000, 1;
end;
}

Okay lastly, we're going to add 1 more while() loop, but this time, we're are going to be replacing those delitem() commands with the loop. The difference is, instead of using .@i we're going to switch it to .@j it is a lower-case J. If your having a hard time seeing that. So, taking a look at our previous loop as an example. Let's replace those delitem commands with a loop:

while( .@j < getarraysize(.required_items) ){
    delitem .required_items[.@j], .required_amount[.@j];
    .@j++;
}

Okay, so now your script should look like mine, or something close to it:

prontera,154,180,4    script    Vending Machine    2_VENDING_MACHINE1,{
.@npc_name$ = "[^FF0000 Vending Machine ^000000]";
mes .@npc_name$;
mes "I will give you "+ .reward_amount +" "+ getitemname(.reward_item) +",";
mes "for the following items:";
while( .@i < getarraysize(.required_items) ){
mes ""+ getitemname(.required_items[.@i]) +" x"+ .required_amount[.@i] +"";
.@i++;
}
switch( select("Trade in items", "That's a rip off!") ) {
    case 1:
        if( (countitem(.required_items[0]) < .required_amount[0]) ||
         (countitem(.required_items[1]) < .required_amount[1]) ||
         (countitem(.required_items[2]) < .required_amount[2]) ||
         (countitem(.required_items[3]) < .required_amount[3]) ||
         (countitem(.required_items[4]) < .required_amount[4]) ){
            mes "You don't have all the required items.";
            close;
        }
        if( !checkweight(.reward_item, .reward_amount) ){
            mes "You don't have enough free inventory space or weight to receive this item.";
            close;
        }
        while( .@j < getarraysize(.required_items) ){
            delitem .required_items[.@j], .required_amount[.@j];
            .@j++;
        }
        getitem .reward_item, .reward_amount;
        break;
}
close;


OnInit:
.reward_item = Valkyrie_Helm;
.reward_amount = 1;
setarray .required_items[0], Red_Potion, White_Potion, Blue_Potion, Jellopy, Poring_Card;
setarray .required_amount[0], 10, 5, 1, 1000, 1;
end;
}

Go ahead and save that, because we are done.

Basic Floating Rates Script - Complete ( 2 Parts )

Here we will create a " floating " npc. And have it adjust base & job experience rates.

Basic Floating Rates Script - Introduction to floating NPCs ( Part 1 )

Alright, so we're going to start off by making a floating npc header. It's the same as a regular npc header, with the exception of replacing the <map_name>,<x>,<y>,<facing> with a simple hyphen ( - ), and the <sprite_id> with -1. So let's make our floating npc header and call our npc " floating_rates ":

-    script    floating_rates    -1,{

Alright, since this is our third script in the beginner's series, we're going to speed things up a bit by not taking so much time to explain things ( this sentence being the exception =P ).

Now, since we want our script to activate everyday of the week, and change daily, we are going to use a label called:  OnClock<hour><minute>:. The time we are going to use is 0000:

OnClock0000:

Okay, now it's time to start adding in where we change the experience rates, but first we need to set 2 variables to a random value between 150 & 200.

 To do so we use this command: rand(<number>{,<number>}); We're going to use scope variables, and we're going to call them base_exp & job_exp, so go ahead and add that beneath our OnClock label:

.@base_exp = rand(150,200);
.@job_exp = rand(150,200);

So, we got that taken care of. Now we are going to actually change the base & job experience rates. To do that we need to use this command: setbattleflag "<battle flag>",<value>;

A list of battle flags can be found in the files listed here: Battle Flags. But we are only going to be using the following flags: base_exp_rate & job_exp_rate .

setbattleflag "base_exp_rate",.@base_exp;
setbattleflag "job_exp_rate",.@job_exp;
Alright, that's everything we need to do, so let's end the script and close it up:
end;
}

When your done, your script should look something like this:

-    script    floating_rates    -1,{
OnClock0000:
.@base_exp = rand(150,200);
.@job_exp = rand(150,200);
setbattleflag "base_exp_rate",.@base_exp;
setbattleflag "job_exp_rate",.@job_exp;
end;
}

And, now when ever your players kill a monster they will get that modified experience rate, that changes every day at midnight.

Basic Floating Rates Script - Fine tuning ( Part 2 )

Okay, so last lesson, we made a very basic floating rates npc that changes the rates every day. However, now we are going to change that npc to only change the rates during weekends.

Let's start by opening up our script:

-    script    floating_rates    -1,{
OnClock0000:
.@base_exp = rand(150,200);
.@job_exp = rand(150,200);
setbattleflag "base_exp_rate",.@base_exp;
setbattleflag "job_exp_rate",.@job_exp;
end;
}

So, to make it only work for Friday, Saturday, and sunday. We are going to add a check for the current day of the week. To do so we will be using this command: gettime(<type>), and we'll be using type 4. To be more specific, we are going to change the rates Friday at midnight, and change them back on Monday at midnight. So let's add the following:

if( gettime(4) == 5 ) {

Now let's put all the data inside that check except for end; and close it up. After that, we are going to add another check using else if () and check to see if it's Monday. Should look like this:

if( gettime(4) == 5 ) {
.@base_exp = rand(150,200);
.@job_exp = rand(150,200);
setbattleflag "base_exp_rate",.@base_exp;
setbattleflag "job_exp_rate",.@job_exp;
} else if( gettime(4) == 1 ) {

Okay, now in the second check, we are just going to set the base & job experience rates back to normal, to do that just use 100 for the value, don't forget to close it up:

setbattleflag "base_exp_rate",100;
setbattleflag "job_exp_rate",100;
}

Right now your script should look something like this:

-    script    floating_rates    -1,{
OnClock0000:
if( gettime(4) == 5 ) {
    .@base_exp = rand(150,200);
    .@job_exp = rand(150,200);
    setbattleflag "base_exp_rate",.@base_exp;
    setbattleflag "job_exp_rate",.@job_exp;
} else if( gettime(4) == 1 ) {
    setbattleflag "base_exp_rate",100;
    setbattleflag "job_exp_rate",100;
}
end;
}

Alright, as of now it's almost done. Let's just edit it a bit, so that we let player's know when the experience rates change and what they changed to.

To do that we are going to use this command: announce "<text>",<flag>{,<fontColor>{,<fontType>{,<fontSize>{,<fontAlign>{,<fontY>}}}}}; in combination with this command: getbattleflag("<battle flag>")

The flag we'll be using is bc_all with 0x00FFFF as the color. For other flags look here: Broadcast Flags additionally, you can use other hex color codes for the broadcast color in this format:  0xRRGGBB

Now then let's add 1 announcement when we increase the rates, and 1 adjustment when we set the rates back:

announce "Bonus Experience Weekend has just started! Base Exp: +"+ (getbattleflag("base_exp_rate") - 100) +"% | Job Exp: +"+ (getbattleflag("job_exp_rate") - 100) +"% !!",bc_all,0x00FFFF;

and

announce "Bonus Experience Weekend has ended.",bc_all,0x00FFFF;

Your script should look something like this:

-    script    floating_rates    -1,{
OnClock0000:
if( gettime(4) == 5 ) {
    .@base_exp = rand(150,200);
    .@job_exp = rand(150,200);
    setbattleflag "base_exp_rate",.@base_exp;
    setbattleflag "job_exp_rate",.@job_exp;
    announce "Bonus Experience Weekend has just started! Base Exp: +"+ (getbattleflag("base_exp_rate") - 100) +"% | Job Exp: +"+ (getbattleflag("job_exp_rate") - 100) +"% !!",bc_all,0x00FFFF;
} else if( gettime(4) == 1 ) {
    setbattleflag "base_exp_rate",100;
    setbattleflag "job_exp_rate",100;
    announce "Bonus Experience Weekend has ended.",bc_all,0x00FFFF;
}
end;
}

We are almost done. The last thing were going to do, is add 1 more announcement that occurs every hour, to let player's know it is bonus exp weekend.

To do that we're going to use the label: OnMinute<minute>: and we are going to use 00 as the minute:

OnMinute00:
if( !gettime(4) || gettime(4) >= 5 ){
    announce "Bonus Experience Weekend in progress! Base Exp: +"+ (getbattleflag("base_exp_rate") - 100) +"% | Job Exp: +"+ (getbattleflag("job_exp_rate") - 100) +"% !!",bc_all,0x00FFFF;
}
end;

When your all done your script should look like this or close to it:

-    script    floating_rates    -1,{
OnClock0000:
if( gettime(4) == 5 ) {
    .@base_exp = rand(150,200);
    .@job_exp = rand(150,200);
    setbattleflag "base_exp_rate",.@base_exp;
    setbattleflag "job_exp_rate",.@job_exp;
    announce "Bonus Experience Weekend has just started! Base Exp: +"+ (getbattleflag("base_exp_rate") - 100) +"% | Job Exp: +"+ (getbattleflag("job_exp_rate") - 100) +"% !!",bc_all,0x00FFFF;
} else if( gettime(4) == 1 ) {
    setbattleflag "base_exp_rate",100;
    setbattleflag "job_exp_rate",100;
    announce "Bonus Experience Weekend has ended.",bc_all,0x00FFFF;
}
end;
    
OnMinute00:
if( !gettime(4) || gettime(4) >= 5 ){
announce "Bonus Experience Weekend in progress! Base Exp: +"+ (getbattleflag("base_exp_rate") - 100) +"% | Job Exp: +"+ (getbattleflag("job_exp_rate") - 100) +"% !!",bc_all,0x00FFFF;
    }
end;
}

Save it and we are done. You just finished creating a Floating Rates NPC.

 

- Intermediate Scripts -

Scripts for intermediate level scripters. If you completed my beginner script series, then you are ready for this section.

Advanced Healer & Buffer Script - Complete ( 2 Parts )

An advanced version of the Basic Healer & Buffer script.

Advanced Healer & Buffer Script - Introduction to Conditional Operators & String/Menu manipulation ( Part 1 )

Okay, so let's start by opening up the final version of our Basic Healer & Buffer Script.

prontera,150,180,4    script    Healer    4_F_ARUNA_POP,{
mes "[^0000FF Healer ^000000]";
mes "Would you like me to heal you?";
mes "^FF0000 *Note - It costs "+ .heal_price +" zeny to get healed.* ^000000";
mes "^FF0000 *Note - AGI-UP + BLESSING Level 10 costs "+ .buff_price +" zeny.* ^000000";
switch( select("Yes, heal me.", "I want buffs", "No thank you") ) {
    case 1:
        if( Zeny < .heal_price ) {
            mes "I'm sorry, but you don't have enough zeny.";
            close;
        }
        Zeny -= .heal_price;
        percentheal 100,100;
        break;
    
    case 2:
        if( Zeny < .buff_price ) {
            mes "I'm sorry, but you don't have enough zeny.";
            close;
        }
        Zeny -= .buff_price;
        sc_start SC_BLESSING,300000,10;
        sc_start SC_INC_AGI,300000,10;
        break;
}
close;

OnInit:
.heal_price = 1000;
.buff_price = 5000;
end;
}

So what we're going to do here is edit our script quite a bit. But firstly, we are going to make the zeny price for healing / buffing optional. To do that is simple. We just need to put our current zeny checks and zeny payment lines, into another if(statement) that simply checks to see if we set a variable above 0. The variable we are going to use is our .heal_price and .buff_price. So let's make those changes:

if( .heal_price ){
if( Zeny < .heal_price ) {
mes "I'm sorry, but you don't have enough zeny.";
close;
}
Zeny -= .heal_price;
}

and

if( .buff_price ){
if( Zeny < .buff_price ) {
    mes "I'm sorry, but you don't have enough zeny.";
    close;
}
Zeny -= .buff_price;
}

That takes care of the first part. Now this is where we get a little technical. We are going to add another variable that gets set every time the server starts / reloads. We are going to call it .include_buffs. And let's just set it to 1 since it's going to be a simple toggle on/off setting. Also, make sure to place it above the other variables we set beforehand.

.include_buffs = 1;

Now this may not seem obvious, but this setting is going to let us decide whether or not players receive buffs when they pay for heals ( free of charge ). Assuming we are all kind admins. So let's add a check in case 1:. It's going to check if the variable is set to 0. And then simply wrap the existing break; command inside that check:

if( !.include_buffs ){ break; }

Doing this will let the script run on through case 1: to case 2:. Next is a bit tricky, we are going to modify the way we set our .buff_price variable. To be more specific, we are going to use a (?: ) check, in combination with the command: atoi("<string>"). For more information regarding (?: ) look here: Conditional Operator. Otherwise let's make the following changes:

.buff_price = ( (.include_buffs)?0:5000 );

Okay so this little bit of code is in short, just going to set the variable to 0. If we include buffs when they buy heals, otherwise set the variable to 5000 which is what we said buffs costs normally.

Alright, so we only have 2 more change to make, and that is simply 1 more check to determine whether or not we show them the buff price. Obviously, if buffs come with heals, we are going to just not show it so lets simply wrap the mes command dealing with showing buff pricing, into a check to see if .include_buffs is 0, additionally let's make that check also check to see if .buff_price is above 0.

if( !.include_buffs && .buff_price ){ mes "^FF0000 *Note - AGI-UP + BLESSING Level 10 costs "+ .buff_price +" zeny.* ^000000"; }

While we're at it, let's add a similar check for the mes command dealing with showing us the price for heals, but without the .include_buffs part:

if( .heal_price ){ mes "^FF0000 *Note - It costs "+ .heal_price +" zeny to get healed.* ^000000"; }

And lastly, we are going to edit our menu. Again, this time we are going to use a (?: )check. We are going to do a little trick where we push menu options down 1 slot. So let's edit our menu:

switch( select("Yes, heal me.", ( .include_buffs?"::":"I want buffs" ), "No thank you") ) {

What this effectively allows us to do, is hide our option to choose " I want buffs " while still making sure the option " No thank you ", which is now shown as option 2, doesn't get directed to case 2: since it is used for buffs only. And that's it. Your script should look something like this when you're done:

prontera,150,180,4    script    Healer    4_F_ARUNA_POP,{
mes "[^0000FF Healer ^000000]";
mes "Would you like me to heal you?";
if( .heal_price ){ mes "^FF0000 *Note - It costs "+ .heal_price +" zeny to get healed.* ^000000"; }
if( !.include_buffs && .buff_price ){ mes "^FF0000 *Note - AGI-UP + BLESSING Level 10 costs "+ .buff_price +" zeny.* ^000000"; }
switch( select("Yes, heal me.", ( .include_buffs?"::":"I want buffs" ), "No thank you") ) {
    case 1:
        if( .heal_price ){
            if( Zeny < .heal_price ) {
                mes "I'm sorry, but you don't have enough zeny.";
                close;
            }
            Zeny -= .heal_price;
        }
        percentheal 100,100;
        if( !.include_buffs ){ break; }
    
    case 2:
        if( .buff_price ){
            if( Zeny < .buff_price ) {
                mes "I'm sorry, but you don't have enough zeny.";
                close;
            }
            Zeny -= .buff_price;
        }
        sc_start SC_BLESSING,300000,10;
        sc_start SC_INC_AGI,300000,10;
        break;
}
close;

OnInit:
.include_buffs = 1;
.heal_price = 1000;
.buff_price = ( (.include_buffs)?0:5000 );
end;
}

So there we go, we now just made the Zeny Price for Heals & Buffs optional. As well as made it so buffs can be included with heals. Lastly, this will work for free should you choose to =P

Advanced Healer & Buffer Script - ( Part 2 )

Okay, here we're going to make it so, players in a guild receive additional buffs, at no extra cost. So let's open up the script from before:

prontera,150,180,4    script    Healer    4_F_ARUNA_POP,{
mes "[^0000FF Healer ^000000]";
mes "Would you like me to heal you?";
if( .heal_price ){ mes "^FF0000 *Note - It costs "+ .heal_price +" zeny to get healed.* ^000000"; }
if( !.include_buffs && .buff_price ){ mes "^FF0000 *Note - AGI-UP + BLESSING Level 10 costs "+ .buff_price +" zeny.* ^000000"; }
switch( select("Yes, heal me.", ( .include_buffs?"::":"I want buffs" ), "No thank you") ) {
    case 1:
        if( .heal_price ){
            if( Zeny < .heal_price ) {
                mes "I'm sorry, but you don't have enough zeny.";
                close;
            }
            Zeny -= .heal_price;
        }
        percentheal 100,100;
        if( !.include_buffs ){ break; }
    
    case 2:
        if( .buff_price ){
            if( Zeny < .buff_price ) {
                mes "I'm sorry, but you don't have enough zeny.";
                close;
            }
            Zeny -= .buff_price;
        }
        sc_start SC_BLESSING,300000,10;
        sc_start SC_INC_AGI,300000,10;
        break;
}
close;

OnInit:
.include_buffs = 1;
.heal_price = 0;
.buff_price = ( (.include_buffs)?0:5000 );
end;
}

Alright, now let's add a mes command near the top that lets players know that they get more buffs if they are in a guild, also let's use ^0000FF as the color so it stands out:

mes "^0000FF *Note - Players in a guild receive additional buffs at no extra charge.* ^000000";

Next, beneath where we give them Increase AGI and Blessing, we are going to add a check to see if they are in a guild. Any guild doesn't matter so long as they are in one. To do that we will need to use this command: getcharid(<type>{,"<character name>"})

if( getcharid(2) ){
Now we just need to give them more buffs. You can add as many as you like, but for this tutorial we're just going to give them Imposito Manus Level 5 and Assumptio Level 5:
    sc_start SC_IMPOSITIO,300000,5;
    sc_start SC_ASSUMPTIO,300000,5;
}

And that's it. We didn't do much here, but that's because the brunt of the work was done a long time ago. Now we are just expanding it with additional features. So your script should look something like this:

prontera,150,180,4    script    Healer    4_F_ARUNA_POP,{
mes "[^0000FF Healer ^000000]";
mes "Would you like me to heal you?";
if( .heal_price ){ mes "^FF0000 *Note - It costs "+ .heal_price +" zeny to get healed.* ^000000"; }
if( !.include_buffs && .buff_price ){ mes "^FF0000 *Note - AGI-UP + BLESSING Level 10 costs "+ .buff_price +" zeny.* ^000000"; }
mes "^0000FF *Note - Players in a guild receive additional buffs at no extra charge.* ^000000";
switch( select("Yes, heal me.", ( .include_buffs?"::":"I want buffs" ), "No thank you") ) {
    case 1:
        if( .heal_price ){
            if( Zeny < .heal_price ) {
                mes "I'm sorry, but you don't have enough zeny.";
                close;
            }
            Zeny -= .heal_price;
        }
        percentheal 100,100;
        if( !.include_buffs ){ break; }
    
    case 2:
        if( .buff_price ){
            if( Zeny < .buff_price ) {
                mes "I'm sorry, but you don't have enough zeny.";
                close;
            }
            Zeny -= .buff_price;
        }
        sc_start SC_BLESSING,300000,10;
        sc_start SC_INC_AGI,300000,10;
        if( getcharid(2) ){
            sc_start SC_IMPOSITIO,300000,5;
            sc_start SC_ASSUMPTIO,300000,5;
        }
        break;
}
close;

OnInit:
.include_buffs = 1;
.heal_price = 0;
.buff_price = ( (.include_buffs)?0:5000 );
end;
}

That's it for our healer, we probably won't need to come back to this ever again =P.

 

*Note - In most of these tutorials, I will be using the script from the previous lesson, unless the scripts deviate from each other. For instance jumping from Healer -> Quest NPC will result in a blank script. But each lesson from then on will be towards the quest NPC and will use the script from the previous lesson. *

 

The idea behind this topic, is for new users, and current ones, to have a (second)place they can go to for reference when trying to write a script if they can't figure it out with script_commands.txt file. It will also help people learn how to write scripts. While hopefully, keeping script writing techniques to a ' very ' similar structure!

 

!! This topic will be updated at Hercules first and then here once a script is fully complete. Some scripts may not be here right away. If this is the case it is because scripts are incompatible and need to be adjusted. !!

Edited by GmOcean
  • Upvote 2
Link to comment
Share on other sites

4 answers to this question

Recommended Posts


  • Group:  Forum Moderator
  • Topic Count:  93
  • Topics Per Day:  0.02
  • Content Count:  10013
  • Reputation:   2346
  • Joined:  10/28/11
  • Last Seen:  

It's good to see member posting some guides to help each others and sharing the technique / knowledges.

 

But I dont know is it only me the only one who felt like this:

  • content is so long, kinda lazy to read through
  • messy
  • hard to read

 

My suggestion:

  • use [ code ] tag for script or pastebin
  • split to different posts / topics.
  • use [ hr ] tag to create a divider

 

We can help you lock this topic to avoid other from posting here if you want.

We could help you split/edit the contents to different post within same topic too.

 

** I will hide this post after you read it. or your reply if you want too.

Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  31
  • Topics Per Day:  0.01
  • Content Count:  666
  • Reputation:   93
  • Joined:  04/27/12
  • Last Seen:  

Yeah, sorry, when I copy + pasted it, the code tags vanished. Just finished adding them in =P

@ Idea to save posts and such, I don't really mind using just 1 post for the whole content, but it's up to you moderators on what you think would be best. I'm just trying to submit helpful stuff for the community.

Edit:

If you decide that multiple posts are better for this topic, then perhaps just limiting it to 4 posts.

1. Basic Scripts

2. Intermediate Scripts

3. Advanced Scripts

4. Expert Scripts

Edited by GmOcean
Link to comment
Share on other sites


  • Group:  Forum Moderator
  • Topic Count:  93
  • Topics Per Day:  0.02
  • Content Count:  10013
  • Reputation:   2346
  • Joined:  10/28/11
  • Last Seen:  

I see, I thought that might be better to each post for each category of script .. haha 

nvm, just follow the way you comfortable with.

Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  162
  • Topics Per Day:  0.05
  • Content Count:  1546
  • Reputation:   192
  • Joined:  07/23/14
  • Last Seen:  

Cool!

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Answer this question...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...