1. tinyromeo

    tinyromeo Active Member

    Messages:
    77
    Intro -
    First you will need a text editor, notepad++ is what I use and recommend.
    Next you will need a game, Eve Online is what I use and recommend, however, I will be teaching towards usefulness for all games.
    Then you will need Innerspace, it is what I use and recommend.
    Optionally you can get an extension for your game (if one exists) to enhance the amount of options you have available to you, ISXEve is what I use and recommend
    Finally open up your LavishScript wiki in another tab or window so you can easily follow along, but I will try to link when possible.

    Start your game with innerspace and press the tilde key to open the console (The little ` next to the number 1). Type the following and press enter:
    Code (Text):
    echo Hello
    There you have it, it is almost that easy.
    echo is very useful for finding out why something isn't working, or if you need to know more about a situation.
    Code (Text):
    echo ${Session}
    Gives the session title
    If you have an extension loaded (using ext isxeve or some other extension) you generally have access to character information through the use of Me
    Code (Text):
    echo ${Me.Name}
    Should return your name when an extension is loaded (however not all extensions are the same so don't *expect* it to work)

    Well thats fairly simply, so what do I do with that? Well lets turn it into an actual script.
    Open Notepad++ (or whatever text editor you chose) and enter the following:
    Code (Text):

    function main()
    {
     echo Hello
    }
     
    save this file in the folder innerspace/scripts (normally found in C:/Program Files(x86)/Innerspace/Scripts) as hello.iss
    Back in game type into the console
    Code (Text):
    run hello
    There you have it... your first script. You can see that in addition to the echo we are now also defining a main function. The main function is required for all scripts, it is the starting point from which everything else branches off, and it is what keeps your script running. Without you will get an error when you try to run your script. Also notice the {}, these tell the script what all the function encompasses. These are also necessary, without them you will get some really funny looking errors.
    One thing I recommend to get in the habit of now is to open and close all parenthesis, brackets, quotation marks, any thing that opens, close it immediately and then fill in the inside. This will help you to keep track of where your brackets are when you start using more complex calls, i.e
    Code (Text):
    ${Math.Calc[${Me.Ship.Module[${Slot}].CurrentCharges} - ${Me.Ship.Module[${Slot}].MaxCharges}]}
    Build something like this in steps - example (ignore the complexity and watch the simple process)
    Code (Text):

    ${}
    ${Math.Calc[]}
    ${Math.Calc[${} - ${}]}
    ${Math.Calc[${Me.Ship[].CurrentCharges} - ${Me.Ship[].MaxCharges}]}
    ${Math.Calc[${Me.Ship[${Slot}].CurrentCharges} - ${Me.Ship[${Slot}].MaxCharges}]}
     
    You can see that by completing the brackets ahead of time you will save yourself the trouble of trying to count them out at the end of the statement.
    Again ignore all the stuff you don't understand, just remember to complete your brackets before hand and this will save you tons of trouble in the end.

    Ok so what now? Next we need to know how to store information for later. We do this using variables. Variables can be many things, numbers, letters or words, collections of numbers, or entire objects with entire datasets all their own. In order to use a variable you have to tell the script to set aside a little bit of memory for this data. To do this we need to declare it. So in the first script change it to match this
    Code (Text):

    function main()
    {
     variable int Number
     echo Hello
    }
     
    When declaring a variable you must tell the script what type of variable it is (in this case int) and what its name is (in this case Number). You can name a variable pretty much anything you want, and they are case specific (Name is different than name).
    The types of variables range widely (see Object Types for a larger listing(also your individual extensions add more)), but it boils down to a few main ones

    bool - Boolean value, TRUE or FALSE only, like a light switch it is only on or off.
    int - Integer, any whole number, 1, 45, -9000, its range is about -2 billion to +2 billion
    float - Any rational number, 3.14, 45628.893728, great when you need decimal points(note: use int64 for a higher range on whole numbers not float)
    string - a sequence of letters and/or numbers (Hello is a string, Forty2 is a string)
    index - is a collection of multiple objects, such as index:int index:string index:entity, an index is useful for sifting through collections of data, like all the enemies in your view, all the items in your bag, etc

    I will go through each of these in time for now lets code.
    Code (Text):

    function main()
    {
     variable int Number = 4
     echo ${Number}
     Number:Set[3]
     echo ${Number}
    }
     
    Above-
    when you run this it will output the value of Number (4). You can see that we can set a variable's value on declaration. Then we access it by ${Number}. Anytime you need access to the data stored inside you use ${Number}
    Then you can see I am changing the value of Number and displaying it again. I change the value by using the int "method" Set. Methods are accessed by a colon : ( i.e. Number:Set)

    If you look inside the wiki for int you will see you have access to different "members" and "methods" of the int data type.
    Again members are accessed with ${Number.Member} - Methods with Number:Method
    Members access stored data, methods perform a task.

    One of the members an int has is Float, meaning you can display this number as a float instead, and since ${Number} is an int it too has access to the Float member - ${Number.Float}

    Some methods to note are Set, Inc, and Dec.
    :Set - will set the number to the new number given - Number:Set[3]
    :Inc - will increase the value by 1 or the number given - Number:Inc - Number:Inc[20]
    :Dec - Opposite of Inc, Decreases value - Number:Dec - Number:Dec[20]

    Neat thing about this is we can put any other numbers into these methods
    Code (Text):

    function main()
    {
     variable int Number = 4
     variable int NumberB = 2
     echo ${Number}
     echo ${NumberB.Float}
     Number:Inc[${NumberB}]
     echo ${Number}
     NumberB:Set[${Number}]
     echo ${NumberB}
    }
     

    Next lets look at float
    Code (Text):

    function main()
    {
     variable float Number = 48.35
     echo ${Number}
     Number:Set[-0.0003]
     echo ${Number}
    }
     
    Pretty much same as before, but float has access to more members than int does and I wanted to make sure you understand members and methods before really moving forward

    float has the same methods - Set, Dec, Inc
    But has many more ways of interpreting its more complex data
    Code (Text):

    function main()
    {
     variable float Number = 321.1234567
     variable float NumberB = 123.987654
     echo ${Number.Int}
     echo ${NumberB.Int}
     echo ${Number.Deci}
     echo ${NumberB.Milli}
     echo ${Number.Round}
     echo ${NumberB.Precision[5]}
     echo ${Number}
     Number:Inc[${NumberB}]
     echo ${Number}
    }
     

    Finally just a dabble on bool, these will be either TRUE or FALSE values, nothing else. They are small so they don't take much memory, and easy to read when we get into control statements later on.

    This all may seem a little excessive, but think of this. Lets say you have your extension loaded and you have access to the Me data set, there are going to be many members and methods that will make more sense to you
    Examples from isxeve Me(character) datatype -
    Code (Text):

    function main()
    {
     echo ${Me.Name}
     echo ${Me.InSpace}
     echo ${Me.AutoPilotOn}
     Me:Undock
     Me:SetVelocity[10]
    }
     

    One last thing on members... They can be cast into many different forms over and over again. Basically, remember that int types have the ability to output floats? and floats can become ints?
    Code (Text):

    function main()
    {
     variable float Number = 4.209
     variable int NumberB = 4
     echo ${Number.Int}
     echo ${Number.Int.Float}
     echo ${NumberB.Float}
     echo ${NumberB.Float.Int}
     echo ${NumberB.Float.Int.Float}
     if ${Number.Int.Float.Equal[${NumberB.Float.Int}]}
     {
      echo "Its true!"
     }
    }
     
    Obviously this is less than ideal to do, but when you need different information check the wiki. Members most of time will link to what their output is so you can see how to interpret the data that you will receive. ${Int.Float} links to the float datatype, ${Float.Int} will link to the int datatype

    Another practical example in isxeve the Character datatype does not have any information on whether or not I am currently in warp, but the Entity Datatype does. So how do we get from a character to an entity?
    Code (Text):

    function main()
    {
     echo ${Me.Name}
     echo ${Me.ToEntity.Name}
     echo ${Me.ToEntity.Mode}
    }
     
    So that is how a character becomes an entity, or a float an int, or anything the datatype allows you to cast into.



    The Next Step - Control



    Alright so we have access to shiny numbers and fancy actions, but how do we use them together?
    We need control statements, things like if, do..while , for.
    Allow me to demonstrate
    Code (Text):

    function main()
    {
     variable int Number = 2
     echo ${Number}
     if ${Number} > 1
     {
      echo "Number is bigger than 1"
     }
     else
     {
      echo "Number is Smaller or equal to 1"
     }
     Number:Dec[1]
     echo ${Number}
     if ${Number} > 1
     {
      echo "Number is bigger than 1"
     }
     else
     {
      echo "Number is Smaller or equal to 1"
     }
    }
     
    Here you can see we are asking if the number we stored is greater than 1. At first it is, after we make it smaller, it isn't. So you can see by making a comparison between two pieces of data can wield different actions. If the first evaluation is true perform the first action inside the {}, otherwise (else) perform the latter action.

    If requires a true or false statement, if 1 > 0, if 10 == 3. Remeber the bool I talked about earlier?
    Code (Text):

    function main()
    {
     variable bool True = TRUE
     variable bool False = FALSE
     if ${True}
     {
      echo "True"
     }
     if ${False}
     {
      echo "False"
     }
     if !${False}
     {
      echo "Not False"
     }
    }
     
    This will display "True" and "Not False"
    Here we really see how if works. if true, do this, if false, don't do this. And finally you can get a statements opposite with the exclamation point !${False} will evaluate to TRUE

    Code (Text):

    function main()
    {
     variable int NumberA = 2
     variable int NumberB = 1
     echo ${NumberA}
     if ${NumberA} > ${NumberB}
     {
      echo "NumberA is bigger than NumberB"
     }
     else
     {
      echo "NumberA is Smaller or equal to NumberB"
     }
    }
     
    Here we are just making the comparison between two stored numbers instead.

    Next we can do many different outcomes in one check
    Code (Text):

    function main()
    {
     variable int Number = 2
     echo ${Number}
     if ${Number} > 1
     {
      echo "Number is bigger than 1"
     }
     elseif ${Number.Equal[1]}
     {
      echo "Number is equal to 1"
     }
     elseif ${Number} < 1
     {
      echo "Number is smaller than 1"
     }
    }
     
    Here you see we have a variety of outcomes we want based on the one value. We can mix and match values pretty much any way we want it, but this will only process the first if to evaluate to true.

    So if you want multiple things to happen do not use else if, simply create another if statement
    Code (Text):

    function main()
    {
     variable int NumberA = 10
     variable int NumberB = 1
     if ${NumberA} > ${NumberB}
     {
      echo "NumberA is bigger than NumberB"
     }
     if ${NumberB} < ${NumberA}
     {
      echo "NumberB is smaller than NumberA"
     }
    }
     

    You can also have multiple comparisons in one statement, requiring that many things are true in order to process
    Code (Text):

    function main()
    {
     variable int NumberA = 2
     variable int NumberB = 1
     if ${NumberA} > 1 && ${NumberB} < 2
     {
      echo "Both are True"
     }
    }
     
    Here I am asking "If NumberA is greater than 1 AND NumberB is less than 2" so in this case BOTH statements must evaluate TRUE (which they will)
    This way we can make sure many things are true before doing something

    In addition to asking "if true AND true" we can ask "if true OR true"
    Code (Text):

    function main()
    {
     variable int NumberA = 2
     variable int NumberB = 1
     if ${NumberA} < 1 || ${NumberB} < 2
     {
      echo "At least one of those is True"
     }
    }
     
    Here obviously NumberA is not less than 1, but NumberB IS less than 2, so here we have an either or approach.

    I won't get to much into it, but you can really tangle some pretty crazy webs as I am sure you are seeing.
    You are also able to combine all these AND's and OR's into parenthesis and really make a mess.
    Code (Text):

    function main()
    {
     variable int NumberA = 2
     variable int NumberB = 1
     if (${NumberA} < 1 || ${NumberA} > 3) || (${NumberB} > 0 && ${NumberB} < 10)
     {
      echo "Something in there is doing something right?"
     }
    }
     
    Here we are making some more complex decisions. Basically this reads
    "if (NumberA is less than 1 OR is greater than 3) OR (NumberB is greater than 0 AND less than 10)" the first question is false, but the second question is true so with the or between the two parenthesis this will evaluate to true. Don't worry if this doesn't quite make sense to you yet. It is rare to do this, and when you are going to ask this kind of question you will probably be more prepared to handle the answer.

    Quickly I want to talk about Equal, I used it once above, but didn't explain. I was informed a while back that Equal is much more effective than ==
    Sounds funny, but I actually prefer using Equal now.
    All the datatypes I explained above have access to Equal and it is the way you should make direct comparisons to data of those types.
    Here is how
    Code (Text):

    function main()
    {
     variable int NumberA = 10
     variable int NumberB = 3
     if ${NumberA.Equal[${NumberB}]}
     {
      echo "B is not equal to A so we won't see this"
     }
     if ${NumberB.Equal[3]}
     {
      echo "3 should equal 3 yes?"
     }
    }
     


    Next I want to show you how to loop. You can create loops a few different ways, but my favorite is "while". Similar to if, while checks a statement, if it is true it will perform its action over and over until the statement is false. This is great when you are monitoring for a change, but be careful to not get caught in an infinite loop, you (almost) always want a way out of the loop. I will demonstrate both and why they might be useful
    First finite loops...
    Code (Text):

    function main()
    {
     variable int Counter = 0
     while ${Counter} < 10
     {
      echo ${Counter}
      Counter:Inc
     }
    }
     
    This code will count up from 0 to 9 and then end.

    Code (Text):

    function main()
    {
     variable int Counter = 0
     while ${Counter:Inc} < 10
     {
      echo ${Counter}
     }
    }
     
    This way will start checking at 1 rather than 0, so it is important to know where you want to start and end your loop, and to be mindful of what the values could/will do while being checked.

    One final way is do...while
    Code (Text):

    function main()
    {
     variable int Counter = 0
     do
     {
      echo ${Counter}
     }
     while ${Counter:Inc} < 10
    }
     
    So this way is similar to the first, it will start at 0, however, this is guaranteed to run at least one cycle before it evaluates false.

    So something like
    Code (Text):

    function main()
    {
     variable bool False = FALSE
     do
     {
      echo "See it runs once even though it is false"
     }
     while ${False}
    }
     

    Here is a tip to help you figure out why your script just plain isn't doing what you want it to
    Code (Text):

    function main()
    {
     variable int NumA = 1
     variable int NumB = 1
     echo "${NumA.Equal[${NumB}]} && ${NumA.Equal[0]}"
     if ${NumA.Equal[${NumB}]} && ${NumA.Equal[0]}
     {
      echo "Why won't this Happen?"
      NumA:Dec
     }
    }
     
    Lets pretend this was more complex and we couldn't figure out why NumA:Dec wasn't happening. By using an echo before the if or while statements you can see what is evaluating true and false and recheck your results from there. In this case we would see an output of "TRUE && FALSE" so we would know that the second expression is evaluating wrong. So you would check all cases of NumA to see if there is a discrepancy in the logic. In this case really all we can say is it should probably say ${NumA.Equal[1]} instead of 0 if we want this if statement to succeed.


    A practical example involving eve would be, say I am docked and I want the script to wait until I am undocked
    Code (Text):

    function main()
    {
     while !${Me.InSpace}
     {
      echo "Waiting to undock"
      wait 10
     }
     echo "Finally in Space"
    }
     
    Here I am using the wait command. This will pause the script for a certain time (in tenths of a second, so wait 10 is wait 1 second) and then continue on.
    So here basically I am checking once a second to see if I am in space, when I finally am the rest of the script can continue.

    One last thing before we can write a real script!!
    ${Math.Calc[]}
    Math.Calc is required to do any sort of arithmetic in lavishscript. In contrast to the methods Inc and Dec, you can do arithmetic on values without changing their values.
    I can only really show by example, but just know all math needs to be done with Math.Calc
    Code (Text):

    function main()
    {
     variable int NumberA = 5
     variable int NumberB = 2
     if ${Math.Calc[${NumberA} - ${NumberB}]} < 4
     {
      echo "5 - 2 is less than 4"
     }
     NumberB:Set[${Math.Calc[${NumberA} + 2]}]
     if ${NumberB} > ${NumberA}
     {
      echo "5 + 2 is greater than 5"
     }
     if ${Math.Calc[5 - 3].Equal[2]}
     {
      echo "does 5 - 3 equal 2?"
     }
     if ${Math.Calc[7 / 3].Int.Equal[2]}
     {
      echo "Math.Calc returns a float, so we can make it into an int if we want"
     }
    }
     
    ${Math.Calc} will return a float, so if you want to maintain that you need to store it in a float. But like above it is just as easy to put it back into an int when you don't care about the decimal point

    Alright, lets make a script that will work in any game supported by Innerspace. Lets make a script that will allow the mouse to "wrap" around the screen. When the mouse reaches the bottom it goes to the top, when it reaches the left it goes to the right, etc

    So we will need to learn more about what properties the mouse has and what methods we can use to modify the mouse...
    An easy way to find the mouse in all of the huge lavishscript wiki is to simply type mouse into the search bar on the side. It will link to 2 main things, but it is easy to find out which you will need. Otherwise lazy asses can go to Mouse
    Alright, lots of useful information in there... Looks like we can get the X and Y positions, then we can move it around, click buttons... holy cow look at all this cool shit in here!!

    Next, since we want this to work on any screen size we will want to know how big our screen is, so again find the display datatype through searching, or else... Display
    Ok there is a ton more information here... all we need are a few values, looks like Width and Height should do it.

    There we are ${Mouse.X}, ${Mouse.Y}, ${Display.Width}, ${Display.Height}. All of them are int which we know pretty well by now.
    And finally Mouse:SetPosition[x,y]
    All pretty straight forward right?
    Well lets mash it all together and see what we can come up with...

    Code (Text):

    function main()
    {
     while 1
     {
      if ${Mouse.X} > ${Math.Calc[${Display.Width} - 2]}
      {
       Mouse:SetPosition[2, ${Mouse.Y}]
      }
      if ${Mouse.X} < 2
      {
       Mouse:SetPosition[${Math.Calc[${Display.Width} - 2]}, ${Mouse.Y}]
      }
      if ${Mouse.Y} > ${Math.Calc[${Display.Height} - 2]}
      {
       Mouse:SetPosition[${Mouse.X}, 2]
      }
      if ${Mouse.Y} < 2
      {
       Mouse:SetPosition[${Mouse.X}, ${Math.Calc[${Display.Height} - 2]}]
      }
     }
    }
     
    Alright first you'll notice while 1, this creates an infinite loop making sure the script never ends, so you will need to stop this script using the endscript command.
    Then all we are doing is checking if the mouse hits one side of the screen and move it to the opposite side. Go ahead try it. Its pretty neat.
    Well how do you feel? You have taken your first steps into a brave new world. Made a fully functioning script. So whats next?
    Well we can dive right in, but I think if you haven't taken the time yet, you should go back over everything, click through the wikis that you are starting to understand a little more.
    Write your own script using what you know, I'll bet there is something you are itching to do with the mouse or keyboard. I bet you could make something, start small, perhaps an auto login script? And it is great practice, don't ever forget that you have a mouse and keyboard when you are swimming in indexes of entities and items.
    Speaking of indexes....


    Indexes, Functions, and more


    I want to show you the index. An index is a great place to store large sets of data of the same type. For instance, all the items in your inventory can be indexed, all the enemies visible to you can be indexed. Once indexed you can look through them with simple loops, making sifting of large datasets simple.
    If you look in the index wiki you will notice there aren't many really useful members or methods readily available to us. The only one I want you to see right now is Insert. As you can guess Insert will add the specified piece of data to the index.
    Still in the wiki in the upper right there is a box labeled "Object Type Vitals" inside you will see that index "inherits" from object container. This means that an index will also have the same members and methods as object container.
    Object container has some more useful stuff in it, like Used, GetIterator, or Clear
    The iterator is used to represent one specific element of the index, then moved to the next element when looping through your checking routine.
    This all sounds complex, but an example will clear everything up

    Code (Text):

    function main()
    {
     variable index:int Numbers
     variable iterator Iterator
     Numbers:Insert[1]
     Numbers:Insert[6]
     Numbers:Insert[78]
     Numbers:Insert[-45]
     Numbers:Insert[1000]
     Numbers:Insert[131]
     Numbers:Insert[-1]

     Numbers:GetIterator[Iterator]
     if ${Iterator:First(exists)}
     do
     {
      echo ${Iter.Value}
     }
     while ${Iterator:Next(exists)}
    }
     
    First we declare our index, since we want a group of ints we say index:int, if you want floats it is index:float, index:string, index:entity, etc
    Then we need to declare an iterator to use for later.
    Then we slide a bunch of numbers into the index (this is the hard manual way, there will be easier ways in your extension)
    Numbers:GetIterator[] will set the Iterator object to the correct index (an iterator doesn't care what is in the index, it only points to the value stored in the index)
    if ${Iterator:First(exists)} This does 2 things, Iterator:First sets the iterator to the first value, and (exists) checks to see if it is there at all.
    while ${Iterator:Next(exists)} Again like above, it switches to the next value, and checks to see if it exists. if it doesn't exists the loop is terminated.
    ${Iter.Value} points to the current value. It could be any of the numbers we inserted into the index at the start, and each pass through the loop will be a different value.

    Here is another example real quick
    Code (Text):

    function main()
    {
     variable index:int Numbers
     variable iterator Iter
     variable int Counter = 0
     
     while ${Counter:Inc} < 20
     {
      Numbers:Insert[${Counter}]
     }
     echo ${Numbers.Used}

     Numbers:GetIterator[Iter]
     if ${Iter:First(exists)}
     do
     {
      if ${Iter.Value.Equal[10]}
      {
       echo "Stopping on 10"
       break
      }
      elseif ${Iter.Value.Equal[2]}
      {
       echo "Number is 2... continuing"
       continue
      }
      echo ${Iter.Value}
     }
     while ${Iter:Next(exists)}
    }
     
    Pretty neat way to make an index right?
    Otherwise you will notice I used break and continue inside the loop this time, break will terminate the loop, continue will ignore everything below it and go on to the next cycle of the loop.

    Well how does this work in game you ask? You want to see the items in your cargo hold you say? Fine, an isxeve example
    Code (Text):

    function main()
    {
     variable index:item Cargo
     variable iterator Iter
     EVE:Execute[OpenCargoHoldOfActiveShip]
     wait 10
     MyShip:GetCargo[Cargo]
     Cargo:GetIterator[Iter]
     if ${Iter:First(exists)}
     do
     {
      echo ${Iter.Value.Name}
     }
     while ${Iter:Next(exists)}
    }
     
    EVE:Execute thing opens the cargohold, which is necessary when reading items inside of it.
    MyShip:GetCargo[index:item] This stores the index of items into the index you supply it
    The rest you know, well should at least.

    Arguments and Strings

    Are almost completely unrelated except for being introduced SIMULTANEOUSLY in this tutorial HERE WE GO
    Code (Text):

    function main(string _MyString)
    {
     variable string MyString = ${_MyString}
     echo ${MyString}
     echo ${MyString.Upper}
     echo ${MyString.Length}
     if ${MyString.Upper.Find[A]}
     {
      echo "MyString contains an a"
     }
     if ${MyString.Lower.Equal[b]}
     {
      echo "MyString is b"
     }
    }
     
    Pretend we saved this file as stringtest.iss
    First thing to change is right up top function main() now has a string declaration in it? This means that instead of run stringtest, we now need to say more. try run stringtest apple or run stringtest b
    The additional information is stored in _MyString, setting it to another string isn't necessary in this example, but there that is how you declare strings... duh
    echo MyString just says it back to you, upper is in all caps, length is well the length.
    Since Upper just returns another string we can switch the statement to all caps and search for a capital a, or conversely switch to lower caps and check for b.

    Code (Text):

    function main(int _VarA = 4, string _VarB = "Auto")
    {
     variable int i
     do
     {
      i:Set[1]
      do
      {
       echo ${_VarB.Mid[${i},1]}
      }
      while ${i:Inc} <= ${_VarB.Length}
     }
     while ${_VarA:Dec} > 0
    }
     
    Here the arguments have default values if none are supplied
    The loop spells out VarB one letter at a time, repeated VarA times.

    Code (Text):

    function main(... Args)
    {
     variable int i = 1
     if ${Args.Size} > 0
     {
      do
      {
       if ${Args[${i}].Find[a]}
       {
        echo ${Args[${i}]}
       }
      }
      while ${i:Inc} <= ${Args.Size}
     }
    }
     
    This one allows any number of arguments to be passed. They are stored in an array named ${Args}
    An array is similar to an index, it stores a set of data. The advantage of an index over an array is the array's size is static, meaning it can not take on more elements after declaration. An index can be dynamically resized at anytime.
    You just need to loop through each item and check its value against what you are looking for.
    Rarely do I use this method, when you start needing this much input it is probably time to learn some GUI stuff, but for the sake of learning there it is.

    Making your own functions...
    Code (Text):

    function main()
    {
     echo "main function"
     call MyFunction
    }

    function MyFunction()
    {
     echo "MyFunction"
    }
     
    Easy!

    However, this now leads me to variable scope
    Code (Text):

    variable(script) int MyGlobalInt
    function main(int _MyLocalInt)
    {
     variable int MyLocalInt = ${_MyLocalInt}
     MyGlobalInt:Set[${MyLocalInt}]

     call MyFunction ${MyLocalInt}

     echo Main
     echo ${MyGlobalInt}
     echo ${_MyFunctionInt}
    }

    function MyFunction(int _MyFunctionInt)
    {
     echo ${_MyLocalInt}
     echo ${MyLocalInt}
     echo ${MyGlobalInt}
     echo ${_MyFunctionInt}
     MyGlobalInt:Set[34]
    }
     
    Seriously study this, and actually run this one.
    Variables can only be accessed in the scope they are available in. This means if I declare a variable in function main(), it is not available for use in function MyFunction().
    You can get around this by creating variables that are available in a wider scope, like the entire script. It is important, however, to be careful about what you want available and where. And for that matter naming things to similar.
    Another way around is passing the variable through the function's arguments also shown above.
    When we start creating our own objects this is going to become very important, but for now just practice keeping your globals limited.

    So what are functions good for anyways? Well if you find yourself performing the same action over and over again you can make life easier by putting that action into a function. So instead of the same three lines haunting you 20 times over, you can slide them into a function for easy using later.
    So I am going to play pretend for a little bit to make explaining this easier. Lets pretend I want to have a bag sorting routine for my fictional game. My fictional extension allows me access to indexes of items, items have a location member, a type member, and a move method. This all sounds pretty realistic right?
    Code (Text):

    function main()
    {
     variable index:item Items
     variable iterator Iter
     Me:GetItemsInBag[Items]
     Items:GetIterator[Iter]
     if ${Iter:First(exists)}
     do
     {
      if ${Iter.Value.Type.Equal["Ammo"]} && !${Iter.Value.Location.Equal["AmmoBag"]}
      {
       call MoveTo ${Iter.Value} "AmmoBag"
      }
      if ${Iter.Value.Type.Equal["Potion"]} && !${Iter.Value.Location.Equal["PotionBag"]}
      {
       call MoveTo ${Iter.Value} "PotionBag"
      }
     }
     while ${Iter:Next(exists)}
    }

    function MoveTo(item _Item, string _Location)
    {
     echo "${_Item.Value.Type} not in ${_Location} - Moving"
     Item:MoveTo[${_Location}]
     wait 10
    }
     
    This obviously will not work with any game I know of, but the principle is the same. You can see how using function MoveTo can start to save you a tremendous amount of work? Not needing to wait on every check, not needing to echo every time, not needing to call moveto everytime, just one simple line to solve all your problems.
    It doesn't save tons of space in the above example, but imagine you have 10 different types of items you want to check, maybe even split stacks as well.

    Or how about a custom wait function that randomizes wait timers, and counts down as well...
    Code (Text):

    function main()
    {
     call RandomWait 2 4
     call RandomWait 5 6
     call RandomWait 3 3
    }

    function RandomWait(int _Range, int _MinLength)
    {
     variable int i = ${Math.Rand[${_Range}]:Inc[${_MinLength}]}
     do
     {
      echo "Waiting - ${i}"
      wait 10
     }
     while ${i:Dec} > 0
    }
     

    One final special type of if... the switch
    Code (Text):

    function main()
    {
     variable index:int Numbers
     variable iterator Iter
     variable int Count = 20
     while ${Count:Dec} > 0
     {
      Numbers:Insert[${Count}]
     }
     Numbers:GetIterator[Iter]
     if ${Iter:First(exists)}
     do
     {
      switch ${Iter.Value}
      {
       case 1
        echo "Num 1"
        break
       case 5
        echo "Cinco"
        break
       case 13
       case 14
       case 15
        echo "Fall through"
        break
       case 12
        echo "More fall through"
       case 11
        echo "Still falling"
       case default
        echo ${Iter.Value}
        break
      }
     }
     while ${Iter:Next(exists)}
    }
     
    The switch will examine the data provided. In this case ints are easily examined. A switch also affords fall through which can be useful in certain scenarios
    If Iter.Value is 1 you will get the output "Num 1"
    if Iter.Vale is 5 you get Cinco
    if it is 13, 14, or 15 you will get fall through
    12 you get More fall through, Still falling, and the number 12
    11 you get Still falling, and the number 11
    Any other number you will just get the number




    Thats all I got in me for now. 12 hours of this in one night is enough. I now expect you to spend your fair share on study! And limitless time on developing.
    Surf the wikis, they make some sense now right?
    Rip apart publicly available scripts and make them your own.
    Build your own scripts from scratch, abuse them until they do what you want them to do.
    When/if I get back we will explore how to make all of this easier through the use of user defined objects, just imagine making your own methods, members, functions. Finally understanding what the pulse method is for. We will explore time. And so much more...

    But I can only show you the links, you must be the one to click on them.
    So for now, go forth and obey your TOS's and your EULA's.
    lilsammy and ladulala like this.
  2. tinyromeo

    tinyromeo Active Member

    Messages:
    77
    Finished 7/31/13

    Code (Text):

    variable(script) objMyObject MyObject

    function main()
    {
     call MyObject.Update
     MyObject:Update
     echo ${MyObject.Update}
    }

    objectdef MyObject
    {
     variable string UpdateMessage = "Message from the object"
     
     function Update()
     {
      echo ${This.UpdateMessage} function
      This:Update
      echo ${This.Update}
     }

     method Update()
     {
      echo ${This.UpdateMessage} method
     }

     member:string Update()
     {
      if 1 == 2
       return "Does Not Equal"
      else
       return "${This.UpdateMessage} member"
     }
    }
     
    Well there you have it. The basics of User defined objects!
    First you have to declare it like any other variable, and remembering scope global is generally good if you need access to it script wide, but I will show how it can be used more locally as well.
    Declaration will fail without defining the object however. objectdef Name is how, and it must be located on the same file or else included with #include filename.iss
    #include can link all sorts of files together so that if you want each object can have its own file, or even if you are doing a lot of #define you can move those to a separate file as well. Just make sure your script can find your object before trying to declare it.
    So include before you declare!
    Then in the main function you can see how we would access the members, methods, and functions OUTSIDE of the object by saying something like MyObject:DoSomething
    INSIDE the object there is something called self reference with "This". Instead of having to say MyObject:DoSomething it is simply This:DoSomething. Both work fine, but one can be easier to type.
    Also since MyObject is a global variable we can also ask it what its stored members are outside of the object... echo ${MyObject.UpdateMessage} will work fine, but telling it to do something won't work... MyObject.UpdateMessage:Set["Fail"]
    You would have to make a seperate method or function for changing those stored variables something like MyObject:SetValueLifeMeaning[42] which would then do something like This.LifeMeaning:Set[${Val}]
    Final word before moving on Atomic
    User defined methods and members must be atomic. This means you cannot call wait whatsoever when something is atomic. The action must complete immediately. You can loop, but very large and infinite looping can have disastrous consequences. When something is atomic it will be processed immediately and nothing else matters, your game will be put on hold while this takes place. Spoken through personal troubles with my newest yamfa rewrite (7/30/13) I found that trying to process to much all at once is getting to be more trouble than its worth, but until the time comes where you are processing hundreds of things a pulse you won't really notice this.
    User defined function on the other hand are not atomic, can use waits, and will generally not hog to much processor power.

    Now I want to show you a cool way to use user defined objects to store and process mass amounts of information very easily.

    Code (Text):

    variable(script) collection:objCoord Coords

    function main()
    {
     Coords:Set[Center,50,50,ffffff]
     Coords:Set[UpperLeft,13,13]
     
     if ${Coords.Element[Center].IsPixel}
     {
      Coords.Element[UpperLeft]:MoveMouse
     }
    }

    objectdef objCoord
    {
     variable int X
     variable int Y
     variable string C
     
     method Initialize(int _X, int _Y, string _C = "000000")
     {
        X:Set[${_X}]
        Y:Set[${_Y}]
        C:Set[${_C}]
     }
     
     member:bool IsPixel()
     {
      if ${Display.GetPixel[${This.X}, ${This.Y}].Hex.Equal[${This.C}]}
      {
       return TRUE
      }
      else
      {
       return FALSE
      }
     }
     
     method MoveMouse()
     {
      Mouse:SetPosition[${This.X}, ${This.Y}]
     }
    }
     
    Firstly I used a collection, these are similar to indexes, they are containers that can be iterated, and also I get the benefit of naming the "key" and having easy access to it with Collection.Element[Key]
    So When I set values in the Coords I need to provide the key first (represents that element) then other additional parameters, those are require by the objects Initialize method
    So "Coords:Set[Key, X, Y, C]" C is actually optional in this case since it has a default value it can use.
    I then gain access to the elements members and methods with "Coords.Element[Key].Value"
    So once again you can see the foundation of user defining and the power it can hold. In fact this little piece here has been foundational to hundreds of my own personal scripts. Build yourself a database of coordinates and colors and you can do just about anything.

    One final example, before I call it quits tonight.
    This one I want to introduce events, onframe, and pulse. I will do this with a practical example from isxeve. I want to keep extension agnostic, but by now you should be able to appreciate what is happening regardless.
    For this example I want a script that will make sure I don't warp away without recalling my drones something that haunts us all.

    Code (Text):

    variable(script) objShip Ship

    function main()
    {
     ;if I haven't shown you by now a semicolon indicates comments
     ;these lines will be ignored and you can add notes to your work
     ;Also notice that this is a "good for nothing" function now
     ;you can still do anything you want here, but by attaching
     ;an atom to an event all the processing is done for you
     ;behind the scenes so to speak
     ;You still need this loop to keep your script running however.
     while 1
      wait 1
    }

    objectdef objShip
    {
     variable int Pulsetimer = 60

     method Initialize()
     {
      ;Initialize happens when you declare the variable
      ;Here we are telling innerspace that whenever our game
      ;renders a new frame we want to call the method Pulse
      ;It doesn't have to be pulse it can be any name, but this
      ;is how you will see it in most scripts
      ;Your extension may also provide something like
      ;Event[ISXEVE_onFrame]
      ;as far as I am aware there is no major difference
      ;I have used both and they both worked as expected
     
      Event[onFrame]:AttachAtom[This:Pulse]
      echo Startup
     }
     
     method Shutdown()
     {
      ;Shutdown happens when the object is being destroyed
      ;typically on script termination
      ;always detach any atoms you have attached
      ;clean up settings, and other misc tidying up
     
      Event[onFrame]:DetachAtom[This:Pulse]
      echo Shutdown
     }
     
     method Pulse()
     {
      ;remember this is called every single frame
      ;generally 60 times per second
      ;we don't want to move that fast
      ;I will leave it up to you how to time pulses
      ;there is Time so perhaps every second
      ;I deal in frame countdown
      ;every frame subtract 1 at 0 process
     
      if ${This.Pulsetimer:Dec} < 1
      {
       This.Pulsetimer:Set[60]
       
       ;Look how simple these checks become
       ;once you layout a solid framework to build on
       
       if ${This.Warping} && ${This.Drones[Space]}
       {
        This:Drones[Recall]
        return
       }
      }
     }
     
     member:bool Warping()
     {
      ;Just because I can
      return ${Me.ToEntity.Mode.Equal[3]}
     }
     
     member:bool Drones(string Loc)
     {
      ;I want a multipurpose member I can call for
      ;any info on drones I want
     
      ;This is almost exactly how I do it in yamfa
      ;Indexes for drones in space and drones in the bay
      ;setup a switch for location possibilities
      ;fill my indexes and iterator
      ;then make the relevant checks and return the result
     
      variable index:item iDrones
      variable index:activedrone aDrones
      variable iterator Iter
      switch ${Loc}
      {
       case Hurt
        Me:GetActiveDrones[aDrones]
        aDrones:GetIterator[Iter]
        if ${Iter:First(exists)}
        do
        {
         if ${Iter.Value.ToEntity.ShieldPct} < 100
          return TRUE
        }
        while ${Iter:Next(exists)}
        break
       case Bay
        MyShip:GetDrones[iDrones]
        if ${iDrones.Used} > 0
         return TRUE
        break
       case Space
       case Idle
        Me:GetActiveDrones[aDrones]
        if ${Loc.Equal[Idle]}
         return ${If[${aDrones[1].State.Equal[0]}, TRUE, FALSE]}
        elseif ${aDrones.Used} > 0
         return TRUE
      }
      return FALSE
     }

     method Drones(string cmd, int64 id = 0)
     {
      ;Again a nice multipurpose method for all my drone needs
      ;again straight out of yamfa
      ;I can skip the in bay index since there is another shortcut
      ;but same thing happening initialize fill and operate on the indexes
     
      variable index:int64 _Drones
      Me:GetActiveDroneIDs[_Drones]
      switch ${cmd}
      {
       case Recall
        EVE:DronesReturnToDroneBay[_Drones]
        return
       case Launch
        MyShip:LaunchAllDrones
        return
       case Guard
        EVE:DronesGuard[_Drones, ${id}]
        return
      }
     }
    }
     
    So script flowthrough... This initializes straight into the pulse event, every frame we are going to check if we should do more processing (optional but highly recommended), then perform one action based on a set of conditions (one action again optional but recommended)
    if the conditions are not met, nothing happens and we try again next round.
    I hope that I have explained everything well enough that you should be able to look and understand exactly what is happening here. It looks complex, but it lays out very easy ways of simplifying your behavior making decisions. Hide all the ugly code checks down below so that making decisions based on your surroundings becomes as easy and simple as possible, reusing code whenever possible.
    Remember if you are doing the same thing over and over again, it is time to make a member or method for it. If you want very complex checks to be reduced to simple boolean statements like above with drones, move it into a nice little member.
    I am not sure if I ever mentioned the If variable. I used it above and it may look very complex, but all it does is return one of two values base on the evaluation of the statement
    echo ${If[1 == 2, TRUE, FALSE]}
    This will return false so it is just a complex looking bool, but it is useful for many different things
    echo ${If[${Me.ToEntitiy.Mode.Equal[3], Warping, Not Warping}]}

    Well I am over typing on this forum for today. Hopefully I didn't move so fast as to lose you, but as you can see you can quickly go from simple login routines to full fledged highly portable scripts in just a few hundred lines.
    All that I feel is really left is to dabble in LavishSettings and LavishUI. I am terrible with UIs, but I can get you started. And there isn't much more to settings you shouldn't be able to figure out yourself by now.
    Thanks again for taking the time to learn this for yourself, now get up and write better scripts than me so I can retire and drink beer.
    Last edited: Jul 31, 2013
    lilsammy and ladulala like this.
  3. tinyromeo

    tinyromeo Active Member

    Messages:
    77
    *just in case*

Share This Page