Jump to content

  •  





Custom AtCommand @buff

Posted by GmOcean, in 2. Intermediate Scripts 27 November 2014 · 1781 views

Custom AtCommand @buff ( 2 parts )
A custom @command, that will buff the player with preset buffs, have a cast time, cost zeny, have a cooldown time and will unlock more buffs the more they use it!

Part 1
Okay, so now it's time for us to make our first @command using a script. Which means we don't need to modify the src code in any way.

Let's start off by making a floating npc:
-    script    at_buff    -1,{

Now we are going to tell the script to create a custom @command for us, to do so we will be using this command: bindatcmd "command","<NPC object name>::<event label>"{,<group level>,<group level char>,<log>};
So let's add ours under OnInit: and call it @buff. For <NPC_object_name>, we'll be using this command to retrieve that: strnpcinfo(<type>).
Lastly, let's call the event label, OnAtBuff:
OnInit:
    bindatcmd "buff", strnpcinfo(3)+"::OnAtBuff";

Now, let's add our other variables. Let's make this command have a cast time of 5 seconds, cost 10.000z to use, and have a cool-down time of 10 minutes. Let's also set a variable for how many tiers of buffs there are, and what the cap tier is.
For this example we'll use 10 as each increment of a tier, and cap it at 2 additional tiers:
    .cast_time = 5;
    .zeny = 10000;
    .cool_down = 600;
    .tiers = 10;
    .buff_level_limit = 2 * tiers;
    end;

Let's move on to our next step. We'll start by making the OnAtBuff: label:
OnAtBuff:

From here we need to first, preform the check for the cool-down, to see if they've used the command recently. We're going to be using this command for that: gettimetick(<tick type>).
But first we need to use a player_variable to check against, we'll call this variable buff_cool_down:
if (buff_cool_down > gettimetick(2)) {

Now that we got the check out of the way, let' sput a message that says "You cannot use this command for another - X amount of minutes.
To do that, we are going to use this command: dispbottom "<message>";
in combination with this function: Time2Str. What that function will do is convert the cool-down into a readable time for us.
To call the function we'll use this command: callfunc "<function>"{,<argument>,...<argument>};
So let's add it all in:
    dispbottom "You cannot use this command for another: "+ callfunc("Time2Str",buff_cool_down) +"";
    end;
}

Let's continue on now that we took care of that, and create our check to see if they have enough zeny. Again, using dispbottom to let them know they don't have enough zeny:
    if (Zeny < .zeny) {
        dispbottom "It costs "+ .zeny +" zeny to use this command.";
        end;
    }

Now it's time to add the cast time for the command. To do so we need to use this command: progressbar "<color>",<seconds>;
    progressbar "0x00FF00", .cast_time;

Okay, now since things in RO are subject to change at any given time, we need to perform the zeny check once more to make sure they didn't some how store their Zeny away. So let's just copy+paste that again, below the progressbar. Also, another reason for doing it this way, is to not make the player suffer through a loading bar when they initially didn't have enough Zeny. That would just be too curel. Unless you like that sort of thing:
    if (Zeny < .zeny) {
        dispbottom "It costs "+ .zeny +" zeny to use this command.";
        end;
    }

Now, all that's left to do is take away the zeny for payment, and add the buffs as needed. To do that we'll be using switch() to make them cycle through the appropriate buffs. We'll determine that by creating a scope_variable that divides a player_variable by .tiers. We also need to increase the player_variable by 1 after receiving buffs, and check to make sure it isn't above the level limit:
    Zeny -= .zeny;
    .@buffs = at_buff_usuage / .tiers;
    switch( .@buffs ){
        case 20:
            sc_start SC_ASSUMPTIO,300000,5;
	
        case 10:
            sc_start SC_IMPOSITIO,300000,5;
	
        case 0:
            sc_start SC_BLESSING,300000,10;
            sc_start SC_INC_AGI,300000,10;
            ++at_buff_usage;
            if (at_buff_usage > .buff_level_limit) {
                at_buff_usage = .buff_level_limit
            }
    }

Now, all we need to do is, add the cool-down as proof they used the command:
    buff_cool_down = gettimetick(2) + .cool_down;
    end;

Also, since that's the end of the script let's close it off. And when you're done, it should look something like this:
-	script	at_buff	-1,{
OnInit:
    bindatcmd "buff", strnpcinfo(3)+"::OnAtBuff";
    .cast_time = 5;
    .zeny = 10000;
    .cool_down = 600;
    .tiers = 10;
    .buff_level_limit = 2 * .tiers;
    end;

OnAtBuff:
    if (buff_cool_down > gettimetick(2)) {
        dispbottom "You cannot use this command for another: "+ callfunc("Time2Str",buff_cool_down) +"";
        end;
    }
    if (Zeny < .zeny) {
        dispbottom "It costs "+ .zeny +" zeny to use this command.";
        end;
    }
    progressbar "0x00FF00", .cast_time;
    if (Zeny < .zeny) {
        dispbottom "It costs "+ .zeny +" zeny to use this command.";
        end;
    }
    Zeny -= .zeny;
    .@buffs = at_buff_usuage / .tiers;
    switch( .@buffs ){
        case 20:
            sc_start SC_ASSUMPTIO,300000,5;
	
        case 10:
    	    sc_start SC_IMPOSITIO,300000,5;
	
        case 0:
  	    sc_start SC_BLESSING,300000,10;
	    sc_start SC_INC_AGI,300000,10;
	    ++at_buff_usuage;
            if (at_buff_usuage > .buff_level_limit) {
                at_buff_usage = .buff_level_limit;
	    }
    }
    buff_cool_down = gettimetick(2) + .cool_down;
    end;
}
And that's it, you just finished making a custom @buff command that everyone in your server can use.

Part 2
Alright, so here we are going to limit the use of this command to certain maps depending on mapflags that are already set in place on those maps. Additionally, we are going to clean the script up a bit.

So let's start by opening up our script again:
-	script	at_buff	-1,{
OnInit:
    bindatcmd "buff", strnpcinfo(3)+"::OnAtBuff";
    .cast_time = 5;
    .zeny = 10000;
    .cool_down = 600;
    .tiers = 10;
    .buff_level_limit = 2 * .tiers;
    end;

OnAtBuff:
    if (buff_cool_down > gettimetick(2)) {
        dispbottom "You cannot use this command for another: "+ callfunc("Time2Str",buff_cool_down) +"";
        end;
    }
    if (Zeny < .zeny) {
        dispbottom "It costs "+ .zeny +" zeny to use this command.";
        end;
    }
    progressbar "0x00FF00", .cast_time;
    if (Zeny < .zeny) {
        dispbottom "It costs "+ .zeny +" zeny to use this command.";
        end;
    }
    Zeny -= .zeny;
    .@buffs = at_buff_usuage / .tiers;
    switch( .@buffs ){
        case 20:
            sc_start SC_ASSUMPTIO,300000,5;
	
        case 10:
    	    sc_start SC_IMPOSITIO,300000,5;
	
        case 0:
  	    sc_start SC_BLESSING,300000,10;
	    sc_start SC_INC_AGI,300000,10;
	    ++at_buff_usuage;
            if (at_buff_usuage > .buff_level_limit) {
                at_buff_usage = .buff_level_limit;
	    }
    }
    buff_cool_down = gettimetick(2) + .cool_down;
    end;
}

To start with, we are going to be adding a new array under OnInit: called .mapflags. In side the array we will add all the mapflags that we wish to use to limit our commands usage.
For a list of mapflags look here: mapflags.
Now then let's add our flags in to limit use in areas that are PvP, GvG or BattleGrounds:
    setarray .mapflags[0],mf_pvp,
                          mf_pvp_noparty,
	       	          mf_pvp_noguild,
		          mf_gvg,
		          mf_gvg_noparty,
		          mf_gvg_castle,
		          mf_battleground;

With that out of the way let's add a check under our OnAtBuff: label to see if the map the player is on, has any of these mapflags on them. To do that we will be using this loop command: for (<variable initialization>; <condition>; <variable update>) <statement>;
in combination with this command to perform the actual check: getmapflag("<map name>",<flag>).
To get the mapname of the player we will use this command: strcharinfo(<type>).
So let's make our loop, and also let the player know that they can't use the command on PvP, GvG and BattleGround maps. This should be no problem for you now, after the few scripts we've done:
   for (.@i = 0; .@i < getarraysize(.mapflags[0]); .@i++) {
  	    if (getmapflag( strcharinfo(3), .mapflags[.@i] )) {
		    dispbottom "This command is disabled on PVP, GVG and BattleGround maps.";
		    end;
	    }
    }

Alright, now we need to do the clean up portion of this edit. To do this, we'll be making a new label at the bottom of our script. We'll call it OnZenyCheck:. Underneath this label we'll add our zeny check we already have in the script. Lastly, beneath the check we will be adding this command: return {<value>}; without the value:
    if (Zeny < .zeny) {
        dispbottom "It costs "+ .zeny +" zeny to use this command.";
        end;
    }
    return;

Now the last ting you need to is replace the other Zeny checks with this command: callsub <label>{,<argument>,...<argument>}; For this we won't be using any arguments, we are just going to call the label, OnZenyCheck
What this will do is instead of typing out the check twice, we simply type it once, and make the script call the check and return to the point of it's execution.
So let's add them in:
callsub OnZenyCheck;

Now, we are done. Your script should look something like this:
-	script	at_buff	-1,{
OnInit:
    bindatcmd "buff", strnpcinfo(3)+"::OnAtBuff";
    .cast_time = 5;
    .zeny = 10000;
    .cool_down = 600;
    .tiers = 10;
    .buff_level_limit = 2 * .tiers;
    setarray .mapflags[0],mf_pvp,
                          mf_pvp_noparty,
		          mf_pvp_noguild,
		          mf_gvg,
		          mf_gvg_noparty,
		          mf_gvg_castle,
		          mf_battleground;
    end;

OnAtBuff:
    for (.@i = 0; .@i < getarraysize(.mapflags[0]); .@i++) {
        if (getmapflag( strcharinfo(3), .mapflags[.@i] )) {
            dispbottom "This command is disabled on PVP, GVG and BattleGround maps.";
            end;
        }
    }
    if (buff_cool_down > gettimetick(2)) {
        dispbottom "You cannot use this command for another: "+ callfunc("Time2Str",buff_cool_down) +"";
        end;
    }
    callsub OnZenyCheck;
    progressbar "0x00FF00", .cast_time;
    callsub OnZenyCheck;
    Zeny -= .zeny;
    .@buffs = at_buff_usuage / .tiers;
    switch( .@buffs ){
	    case 20:
	        sc_start SC_ASSUMPTIO,300000,5;
	
	    case 10:
	        sc_start SC_IMPOSITIO,300000,5;
	
	    case 0:
	        sc_start SC_BLESSING,300000,10;
	        sc_start SC_INC_AGI,300000,10;
	        ++at_buff_usuage;
	        if (at_buff_usage > .buff_level_limit) {
	            at_buff_usuable = .buff_level_limit;
	        }
    }
    buff_cool_down = gettimetick(2) + .cool_down;
    end;

OnZenyCheck:
    if (Zeny < .zeny) {
        dispbottom "It costs "+ .zeny +" zeny to use this command.";
        end;
    }
    return;
}
And that's it, we're done with this script.