# Thread: Procedures & Functions for Beginners

1. ## Procedures & Functions for Beginners

Procedures & Functions for Beginners
By me, Incurable!

Last updated 30th October, 2015

NOTE: I wrote this in a couple of hours and then rewrote the whole thing. I think I got everything but there might still be a few remnants of the previous tutorial leftover. If you spot anything that looks like it shouldn't be there, let me know!

Over the last few months I've noticed that quite a lot of new scripters don't fully understand the difference between procedures and functions and don't know when to use them or how. This brief tutorial aims to explain the difference between procedures and functions and how to use them effectively. It will do this by writing the code for a rudimentary power miner. It assumes that you have already completed The Mayor's AIO scripting tutorial. It's important to note that this tutorial specifically discusses the use of procedures and functions in Lape and other Pascal derived languages, but it can be applied to other languages in a very general context.

Now, let's get started!

Section 1: The Difference

We'll begin with the difference between procedures, functions, methods and routines. You've heard these words before, but the difference probably isn't very clear to you.

Procedure: A (usually small) block of code that acts as a sub-program in a (usually larger) program. It executes its code line-by-line in a procedural fashion until no more lines remain to be executed. It can contain other procedures and functions, but it does not return a value.

Example: This could be used to login a player. Note that the SRL-6 function players[currentPlayer].login() calls another function named isLoggedIn() which means that we don't need to use any if statement to check if we're logged in before calling it.

Simba Code:
procedure LoginPlayer();begin  writeLn('Logging in');   players[currentPlayer].login();  wait(randomRange(2000, 5000));end;

In this case, our procedure will check to see if the player is logged in. If they're not, it will write a line to the Simba debug console, log the player in (if they're not already logged in), and wait for a random time between 2 and 5 seconds. No value has been returned, and we cannot use this procedure as a condition in any test, such as in an if statement or while loop.

Function: A (usually small) block of code that acts as a sub-program in a (usually larger) program. It executes its code line-by-line in a procedural fashion until no more lines remain to be executed. It can contain other procedures and functions and should* return a value.

Example: A simple demonstration of a function that will click on an ore rock and return a boolean defined by the results of the SRL-6 function mainScreen.findObject().

Simba Code:
// clickOre will return a boolean defined by whether or not an ore rock// was found and clicked on with mainScreen.findObjectfunction clickOre(): boolean;var  x, y: integer;begin  writeLn('Clicking ore');  if (mainscreen.findObject(x, y, 7446732, 9, ['opper'], MOUSE_LEFT)) then    result := true;end;

Unlike our procedure, a function can be used in a conditional test. We can actually make this code even shorter by directly calling the mainScreen.findObject() function and assigning its result to our own function's boolean result! Confused? I don't blame you. Here's how we do that:

Simba Code:
// clickOre will return a boolean defined by whether or not an ore rock// was found and clicked on with mainScreen.findObject()function clickOre(): boolean;var  x, y: integer; begin  writeLn('Clicking ore');  result := mainscreen.findObject(x, y, 7446732, 9, ['opper'], MOUSE_LEFT);end;

Now that we've written a function, here's an example of the same function being used in some pseudo-code:

Simba Code:
function mineOre(): boolean;begin  while the backpack isn't full do    if clickOre() then // <--- Here as a condition    begin      write: we found some ore!      wait until ore is mined    end   if backpack is full    result = trueend;

* A function doesn't have to return a value, but it would be bad practice to declare a function and use it as a procedure. If you're not going to return anything then you should be using a procedure instead.

Data types that can be returned: Everything! No really, as far as I know functions in Lape are able to return practically any type that you like. The most common return type in SRL-6 is a boolean, but you can return integers, doubles, strings, and even custom data types! This makes functions incredibly versatile and useful in almost every scenario.

Method/routine: These two words are used to refer to all of the procedures and functions in a program. In other words, if someone mentions that they like your "methods" when talking about your script, they are usually referring to all of your procedures and functions. The same goes for routines. It's worth noting that not everybody uses them this way and may instead confuse them with the word procedure, but when talking about Lape specifically, it's not very useful to refer to a procedure as a method or routine when it is defined by the keyword procedure.

This is a point of confusion for a lot of people that stems from the use of the words method and routine in other languages to refer to their versions of procedures and functions. However, in our usage (Lape) we use procedure and function as actual keywords, so they should not be referred to by any other name (thanks to @the bank; for clarification on this).

Section 2: Correct Usage

Now that we know the difference between procedures and functions we can discuss when you should actually use them. I'm just going to assume that this isn't completely clear to you yet. If it is, awesome, you can skip to section 3.

Procedure: A procedure should be used when you need to execute a block of code but don't need to return any values. For example, our LoginPlayer() procedure wouldn't do us any good as a function because we aren't testing any conditions and will know if it worked by observing the results. We also can't turn it into a function in its current form because we have no conditions to test! Procedures can also be used to create a neater, more readable, and more maintainable script. By placing chunks of code into useful procedures, we can simplify a script and quickly find problems when they arise.

Function: A function should be used when you want to test a condition and return the result of that test. Yup, pretty simple, right? That's because it really is. Because of their versatility, functions can be used in almost any scenario to test conditions and return the result. This is especially useful in RuneScape scripting where we need to know the state of our player and the game world at all times. I'll give you an example.

Example:You're writing a power miner and want to keep mining until the backpack is full. Using a conditional test such as an if statement, we can test whether the backpack is full using the SRL-6 function tabBackpack.isFull(). First, let's take another look at our first example function:

Simba Code:
// clickOre will return a boolean defined by whether or not an ore rock// was found and clicked on with mainScreen.findObject()function clickOre(): boolean;var  x, y: integer;begin  writeLn('Clicking ore');  result := mainscreen.findObject(x, y, 7446732, 9, ['opper'], MOUSE_LEFT);end;

... we can use this function as a test inside of another function that uses a repeat..until loop nested inside of a while. This is the fleshed out version of the pseudo-code example above:

Simba Code:
// This function will mine ore while the SRL-6 function "tabBackpack.isFull()" is// returning false! By using the function as a test condition in a while loop,// we can use its result to know whether or not we need to mine another ore rock!function mineOre(): boolean;var  currentCount: integer; begin  // Using "not" is the same as "while (tabBackpack.isFull() = false) do"  while (not tabBackpack.isFull()) do  begin    currentCount := tabBackpack.count(); // Get the number of items in the backpack    // Again, this is the same as "if (clickOre() = true) then"    if (clickOre()) then    begin      writeLn('Waiting while mining');      // Try and understand what this does and how it works!      repeat        wait(randomRange(100, 250));      until (tabBackpack.count() > currentCount)    end else    begin      writeLn('Cannot find ore, exiting');      exit(false); // See note below for an explanation of this    end;  end;   result := true; // This line is only reachable if tabBackpack.isFull() is true!end;

Did you notice the use of exit(false)? That should be fairly self-explanatory, but here's a quick explanation anyway. The statement exit is used to stop a method and exit out of it completely, moving on to the next step in the program. If a boolean parameter is added when called inside of a function, such as in this case, the result of that function will be the value of that boolean. So, to demonstrate again, the following function will always return true despite result being assigned as false.

Simba Code:
program ExitDemo;function someFunc(): boolean;var  param: boolean;begin  result := false;  if (not result) then    writeLn('result = false');  exit(true);end;begin  clearDebug();  writeLn(someFunc());end.

Try it for yourself. Copy/paste the code into a new program and hit run. What result gets printed out? What happens if you change result to true and the parameter of exit to false?

Section 3: Effective Use

This is a brief sample of SRL-6 functions and their return types:

• tabBackpack.isFull(): boolean;
• tabBackpack.count(): integer;
• bankScreen.isOpen(): boolean;
• bankScreen.open(): boolean;
• collectBox.isOpen(): boolean;
• chatBox.getXPBar(): integer;
• chatBox.getTextOnLine(line): String;
• lootScreen.getSlotBox(slot): TBox;

... as you can see, the majority of methods in the SRL-6 include are functions that return some value relevant to the function. This is done so that we may use these functions to be constantly testing the state of the game world and reacting accordingly. For example, in our mineOre() function we use tabBackpack.isFull() to test whether we should keep mining. Similarly, we use tabBackpack.count() to make the script wait until the backpack count is greater than the value stored in the integer type variable currentCount.

It's important to quickly note that a function used as a condition is always executed and tested in order to get its result. An if statement with the condition not tabBackpack.isFull() will still execute the function tabBackpack.isFull() and use its condition as a result, regardless of whether we use the keyword not. Functions can be used to constantly test conditions and react accordingly. The following is a seemingly complex example of a number of functions being used in an event loop procedure in a smelting script. It is heavily commented to make it easier to understand. It's actually really simple once you understand how it works, but the number of conditionals can be daunting for some.

Simba Code:
procedure EventLoop();begin  // A failsafe to stop the script if the global variable of type boolean  // "IsScriptRunning" is false  if (not IsScriptRunning) then    exit;   players[currentPlayer].login(); // Remember: this only logs in if we aren't logged in already  SetupScreen(); // Setup the screen   ProgRep(); // Write a progress report to the Simba debug console   // Test the result of a lodestone teleporting function  if (IL.lodeTele(LOCATION_LUMBRIDGE)) then    TelesDone += 1; // increment the global variable of type integer "telesDone" by 1   // Test the result of a walking function  if (IL.walkThePath(SPS, PathLodeToBank)) then    repeat      // Test the result of a banking function      if (not openBank()) then      begin        // Report an error since openBank() didn't work. We know this because it returned false.        ReportError('openBank() failed', 'openbank');        break();      end else      begin        // Test the result of an ore withdrawing function        if (not withdrawOre()) then        begin          // Report an error since withdrawOre() didn't work. We know this because it returned false.          ReportError('withdrawOre() failed', 'withdrawore');          IsScriptRunning := false;        end;      end;       // Test the result of a minimap rotating function      if (not minimap.setAngle(gaussRangeInt(288, 318))) then      begin        // Try walking to the furnace instead of setting the angle of the minimap, test the result        if (not IL.walkThePath(SPS, PathBankToFurn)) then // Try walking        begin          // Report an error since IL.walkThePath() didn't work. We know this because it returned false.          ReportError('walkPath(PathBankToFurn) failed', 'walkPath_BankToFurn');          break();        end;      end;       wait(gaussRangeInt(200, 400)); // Wait a period of time between 200-400 seconds randomised to a Gaussian value       // Test the result of a furnace finding function      if (not findFurnace()) then      begin        // Report an error since findFurnace() didn't work. We know this because it returned false.        ReportError('findFurnace() failed', 'findFurnace');        break();      end else        // Since findFurnace() returned true, test the result of a function that smelts the ore        if (not smeltOre()) then        begin          // Report an error since smeltOre() didn't work. We know this because it returned false.          ReportError('smeltOre() failed', 'smeltOre');          break();        end;       // Test the result of a minimap rotating function      if (not minimap.setAngle(gaussRangeInt(18, 38))) then        // Try walking to the bank instead of setting the angle of the minimap, test the result        if (not IL.walkThePath(SPS, PathFurnToBank)) then // Try walking        begin          // Report an error since IL.walkThePath() didn't work. We know this because it returned false.          ReportError('walkPath(PathFurnToBank) failed', 'walkPath_FurnToBank');          break();        end;       wait(gaussRangeInt(200, 400)); // Wait a period of time between 200-400 seconds randomised to a Gaussian value      ProgRep(); // Write a progress report to the Simba debug console      BreakHandler(); // Execute the break handling procedure    until ((not IsScriptRunning) or (not isLoggedIn()));    // ^ The previous repeat..until loop runs until IsScriptRunning is false or the player isn't    // logged in. IsScriptRunning is set to false if any of the condition checks fails.     // Although this code is unreachable at first, it will still only execute if IsScriptRunning is false.    // It will not execute if the player has only logged out for some reason (for example, by disconnecting).    if (not IsScriptRunning) then    begin      writeLn('Ending script, be sure to post the proggy and any debug info ' +      'to the thread! :D');    end;end;

Hopefully that procedure wasn't too complicated and the comments did a good enough job of explaining how multiple functions can be used to test certain conditions and react accordingly.

Section 4: Rudimentary Powerminer Demo

The following code is the complete program that we have written a couple of functions for already: a rudimentary power miner! It should work, so go ahead and copy/paste it or write it out manually (recommended). Once that's done, you can run it after standing at the Varrock East Mine logged in OR out in the following position. Remember that you're running it to try and understand it, not to use as an actual script!

Setup location:

Script:

Simba Code:
program ProcAndFuncTutMiner;{$DEFINE SMART}{$I SRL-6/SRL.simba} // Remember that this is a RUDIMENTARY power miner. It should not be used// for an extended period of time nor should it be used on anything but a// throw away account. It is for demonstration purposes only. var  KeepMining: boolean; // A global variable that will be used as a condition to                       // test whether the script should continue running! procedure DeclarePlayers();begin  setLength(players, 1);  with players[0] do  begin    loginName := 'username';    password := 'password';    isActive := true; // Ignore    isMember := false; // Is the account a member?    world := 0; // Ignore  end  currentPlayer := 0; // Ignoreend; // This procedure will setup the script by executing a series of pre-written// procedures and setting a few boolean variables!procedure SetupScript();begin  smartShowConsole := false; // Disable the SMART console box  SetupSRL(); // Setup the SRL-6 include  disableSRLDebug := true; // Disable the SRL-6 include debug options  clearDebug(); // Clear the Simba debug console so we can see our own writeLn's  DeclarePlayers(); // Declare the player(s) to be used  writeLn('Script setup complete!'); // Write a line to the Simba debug console  KeepMining := true; // Set the global variable "KeepMining" to true for use in                      // the "main" procedureend;  // This procedure acts as a simple wrapper to login and wait 2-5 seconds. It's// actually not totally necessary and just serves as a demo of a potentially// useful procedure.procedure LoginPlayer();begin  if (not isLoggedIn()) then  begin    writeLn('Logging in');    players[currentPlayer].login();    wait(randomRange(2000, 5000));  end;end; // Again, this is just a wrapper for some basic functions. It's not totally// necessary, but serves as a good demo of a potentially useful procedure.// Can you figure out why I didn't combine SetupScreen and LoginPlayer into a// bigger procedure? Or why I didn't include them both in SetupScript?procedure SetupScreen();begin    writeLn('Setting up the screen');    mainScreen.setAngle(MS_ANGLE_HIGH);    mainScreen.setZoom(false);    minimap.setAngle(MM_DIRECTION_SOUTH);end;  // clickOre will return a boolean defined by whether or not an ore rock// was found and clicked on with mainScreen.findObject(). We can use this// boolean in the next function!function clickOre(): boolean;var  x, y: integer; begin  writeLn('Clicking ore');  result := mainscreen.findObject(x, y, 7446732, 9, ['opper'], MOUSE_LEFT);end; // This function will mine ore while the SRL-6 function "tabBackpack.isFull()" is// returning false! By using the function as a test condition in a while loop,// we can use its result to know whether or not we need to mine another ore rock!function mineOre(): boolean;var  currentCount: integer; begin  // Using "not" is the same as "while (tabBackpack.isFull() = false) do"  while (not tabBackpack.isFull()) do  begin    currentCount := tabBackpack.count(); // Get the number of items in the backpack    // Again, this is the same as "if (clickOre() = true) then"    if (clickOre()) then    begin      writeLn('Waiting while mining');      // Try and understand what this does and how it works!      while (tabBackpack.count() = currentCount) do        wait(randomRange(100, 250));    end else    begin      writeLn('Cannot find ore, exiting');      exit(false);    end;  end;   result := tabBackpack.isFull() // Assign the result of the function tabBackpack.isFull()                                 // to this function's resultend; // This is the procedure that will be repeatedly executed in the "while (KeepRunning) do"// loop setup in our main procedure!procedure EventLoop();begin  writeLn('Entering the event loop...');  LoginPlayer();  SetupScreen();  if (mineOre()) then    tabBackpack.dropItems()  else    KeepMining := false;  writeLn('We have reached the end of the event loop...');end; // This procedure will log the player out. Usually you would do some more stuff// in a procedure like this such as freeing DTMs and bitmaps, but since we don't// have any of those, this is just a very basic wrapper for logging out.procedure EndScript();begin  writeLn('Logging out and ending script');  players[currentPlayer].logout();end; // Do you understand what's going on in this main procedure?// Why is it so short?begin    SetupScript();    while (KeepMining) do      EventLoop();    EndScript();end.

... and that's everything! By now you should understand the difference between a procedure and a function and have a good idea of how and when you can utilise them best. If you don't, then it's probably my fault for writing an unclear tutorial! If you have any problems or questions, please don't hesitate to post them here no matter how old this thread is. If you tag me by wrapping my name in tags I will get a notification and be able to answer you sooner rather than never.

Good luck with your scripting adventures, be sure to tag me in your first script if you utilise functions: I want to know if I actually taught anyone anything.
Last edited by Incurable; 03-07-2016 at 05:16 AM.

2. Simba Code:
// Login only if we're not logged in  if (not isLoggedIn()) then    players[currentPlayer].login();
isLoggedIn is called by players[currentPlayer].login(); so no need to use isLoggedIn in your script! Don't be inefficient
Nice work

3. Originally Posted by Harrier
Simba Code:
// Login only if we're not logged in  if (not isLoggedIn()) then    players[currentPlayer].login();
isLoggedIn is called by players[currentPlayer].login(); so no need to use isLoggedIn in your script! Don't be inefficient
Nice work
I didn't know that, thanks. Seems a bit silly, but whateverz.

4. This should be posted outside of RS3 scripting section. An awesome tutorial that doesn't only pertain to RS3, but to scripting in general. I've been on these forums for awhile, and have never come across this tutorial. I even looked for this specific type of tutorial at one point!

5. SRL Member
Join Date
Dec 2010
Posts
483
Mentioned
30 Post(s)
Quoted
328 Post(s)
Originally Posted by Incurable
Method/routine: These two words are used to refer to all of the procedures and functions in a program. In other words, if someone mentions that they like your "methods" when talking about your script, they are usually referring to all of your procedures and functions. The same goes for routines. It's worth noting that not everybody uses them this way and may instead confuse them with the word procedure, but when talking about Lape specifically, it's not very useful to refer to a procedure as a method or routine when it is, in fact, a procedure.

This is a point of confusion for a lot of people and it stems from the usage of methods and routines in other languages (Java is a good example) as actual keywords, but in our case they are not keywords and should not be referred to in place of an actual keyword.
Meh. You're right that the terminology comes from other languages. However, neither routine or method are actual keywords in Java, even if "method" is a popular word when talking about java. A method is essentially a member function, keep in mind that Java forces OOP.

Therefore, one could think of TTimeMarker.getTime() as a method.

Additionally, procedures very well can return something, or multiple things, if you pass by reference rather than passing by value.

6. Originally Posted by the bank
Additionally, procedures very well can return something, or multiple things, if you pass by reference rather than passing by value.
That's when you use the out keyword in the procedure's declaration to specify a variable that data will be outputted to, correct?

7. Originally Posted by KeepBotting
That's when you use the out keyword in the procedure's declaration to specify a variable that data will be outputted to, correct?

8. Originally Posted by KeepBotting
That's when you use the out keyword in the procedure's declaration to specify a variable that data will be outputted to, correct?
Yes, tho out is currently just an alias for var. out is preferred whenever the current value stored in the variable is going to be ignored.
Last edited by slacky; 02-12-2016 at 06:42 PM.

9. SRL Member
Join Date
Dec 2010
Posts
483
Mentioned
30 Post(s)
Quoted
328 Post(s)
Originally Posted by KeepBotting
That's when you use the out keyword in the procedure's declaration to specify a variable that data will be outputted to, correct?
Correct. Essentially, passing by value will make a physical copy of the variable in memory and use that within the scope of the function. Passing by reference passes that exact object in memory (think: pointer) and anything you do to it within the function scope will affect it outside of the scope as well.

10. Originally Posted by the bank
Meh. You're right that the terminology comes from other languages. However, neither routine or method are actual keywords in Java, even if "method" is a popular word when talking about java. A method is essentially a member function, keep in mind that Java forces OOP.

Therefore, one could think of TTimeMarker.getTime() as a method.

Additionally, procedures very well can return something, or multiple things, if you pass by reference rather than passing by value.

You're right, I don't know why I remembered "method" as a keyword. I'll edit that to make it more clear. Thanks!

Originally Posted by Vusn
This should be posted outside of RS3 scripting section. An awesome tutorial that doesn't only pertain to RS3, but to scripting in general. I've been on these forums for awhile, and have never come across this tutorial. I even looked for this specific type of tutorial at one point!
Thank you, I'll see what I can do.

EDIT: I've taken another look around the forum and can't really see where else I would put this. Do you have a suggestion? Where did you go when looking for this kind of tutorial?
Last edited by Incurable; 03-07-2016 at 05:18 AM.