Author Topic: [Tutorial] Advanced Danmakufu Scripting Techniques  (Read 13736 times)

Blargel

  • RAWR!
  • I'M AN ANGRY LOLI!
[Tutorial] Advanced Danmakufu Scripting Techniques
« on: February 03, 2010, 03:44:41 AM »
Helepolis plox teech people how 2 do dis via vidya

This tutorial is for explaining various techniques in programming that Danmakufu supports and when to use them. It assumes that you are already fairly familiar with Danmakufu. If you are not, you should probably take a look at the Tutorial Thread Index or fool around with Danmakufu some more until you are more comfortable with it.

Table of Contents
« Last Edit: February 03, 2010, 04:32:48 AM by Blargel »
<WorkingKeine> when i get home i just go to the ps3 and beat people up in blazblue with a loli
<Azure> Keine: Danmakufu helper by day, violent loli by night.

Blargel

  • RAWR!
  • I'M AN ANGRY LOLI!
Re: [Tutorial] Advanced Danmakufu Scripting Techniques
« Reply #1 on: February 03, 2010, 03:45:32 AM »
Nested Functions, Subs, and Tasks

Before we begin, let me say that I don't want to say "functions, subroutines, and tasks" every time I want to refer to them. I'm going to use "functions" as an encompassing term for all three, but I may sometimes use "function" to refer specifically to functions and NOT subroutines and tasks. I hope the different contexts will make it clear enough which meaning I am using when I say "functions".

What is it?
You are probably already familiar with functions, subroutines, and tasks. You probably use them a lot to avoid writing a lot of repetitive code or make things easier to understand. Or in the case of tasks, you use them to handle some somewhat complicated logic over a period of time. This also probably means that you know that they can use variables that are defined outside of them as well as create their own variables for their own private use. Nothing else outside of the them can access the variables they created.

If you do NOT know these things, you are obviously in the wrong tutorial. Anyway, just like how you can create and use variables in functions, you can also create and use more functions in a function. You can even do a function in a function in a function in a function if you really wanted to, though it becomes incredibly messy and hard to follow if you go overboard with this. And just like variables, functions defined inside functions can only be used by the the function they are defined in.

The benefit to making a function inside a function instead of just putting outside with the rest of the functions is that the inner function can still use the variables defined in the outer function. Also, since it cannot be used outside of the function it is defined in, it can have the same name as another function that is defined in a function.


Why would you use this?
Sometimes, you just want to make sure that a specific function is only used by another function. Using it by itself would spell disaster and you may forget this and do it anyway. The solution? Well what section of this tutorial are we in? Nested functions of course!

Another common situation that this can be useful for is timing multiple things for an object. For example, if you want an object bullet to start fast and gradually slow down, you can use a single task to do that easily. But what if you wanted it to abruptly change angles at a specific time after being fired too? And then change again a second time? And then you want to change graphics too! Oh and how about adding some alpha phasing and all sorts of other pointless neat effects? :V

Okay so that's a rather extreme example, but a relatively easy way would be to have a function that sets up the initial values and then calls multiple tasks to handle each different attribute. This way, each task defined in that function would have access to the variable that holds the object's ID and can manipulate each attribute while using the Wait function in whatever way it wants without interrupting the flow of the other tasks that are managing other attributes.

Lastly, this is a rather simple situation. As you should know, only functions can return values. Subroutines cannot and tasks can use the return; command, but cannot use it to return a value. If you wanted a sub to return a value, all you would need to do is to turn it into a function and put return value; at the end of it. But what if you wanted a task that has multiple yields in it to return a value? For example, if you wanted to create a task that manages an object bullet in some way but also wanted to return the object's ID to a variable while calling it for some reason? The solution is to define the task that does everything in a function and then the function simply creates the object while storing the ID into a variable, calls the task that also has access to the variable with the ID, and then returns the ID.

The best way to learn is by examples so read on if you want to get a better grasp of the concept.

Examples

First, a general example:
Code: [Select]
function OuterFunction {
  Do stuff;
  Do other stuff;
  InnerFunction;

  function InnerFunction {
    Do even more stuff;
    Do one last thing;
  }
}
This is the general setup of what a nested function will look like. If you were to call OuterFunction somewhere, it would run Do stuff, Do other stuff and then run the function InnerFunction which does the last two things. Note that calling InnerFunction by itself would cause an error because only OuterFunction can use it.


Here's an example for handling multiple attributes of an object independently:
Code: [Select]
// I'm assuming you have a Wait function already
function AwesometasticShot(x, y, startspeed, startangle, graphic, delay){
  let obj = Obj_Create(OBJ_SHOT);
  Obj_SetPosition(obj, x, y);
  Obj_SetSpeed(obj, startspeed);
  Obj_SetAngle(obj, startangle);
  ObjShot_SetGraphic(obj, graphic);
  ObjShot_SetDelay(obj, delay);
  ManageSpeed;
  ManageAngle;
  ManageAlpha;
 
  // This task manages the speed to make it slow down to 1/3
  // of the starting speed over a time period of 60 frames
  task ManageSpeed {
    let endspeed = startspeed/3;
    let accel = (endspeed - startspeed)/60;
    while(Obj_GetSpeed(obj)>endspeed && !Obj_BeDeleted(obj)){
      Obj_SetSpeed(obj, Obj_GetSpeed(obj)+accel);
      yield ;
    }
    Obj_SetSpeed(obj, endspeed);
  }
 
  // This task manages the angle to make it curve for 120 frames,
  // stop curving for 60 frames, and then abruptly reverse directions
  task ManageAngle {
    loop(120){
      Obj_SetAngle(obj, Obj_GetAngle(obj)+3.1);
      yield ;
    }
    Wait(60);
    Obj_SetAngle(obj, Obj_GetAngle(obj)+90);
  }
 
  // This task manages the alpha to make it phase in and out with a
  // cos wave for the duration of the shot's existence.
  task ManageAlpha {
    let counter = 0;
    while(!Obj_BeDeleted(obj)){
      Obj_SetAlpha(obj, 128+cos(counter*6)*127);
      counter++;
      yield ;
    }
  }
}
Notice how it's much easier it is to see where the logic for each attribute is. If you were to do the whole thing in one task, it would be a giant mess with one or more counter variables and a lot of if statements. This makes it easier to read, easier to maintain, and easier to remember what you were doing.


Here's the object-managing task that returns a value:
Code: [Select]
function SomeSortOfShot {
  let obj = Obj_Create(OBJ_SHOT);
  TheRealTask;
  return obj;

  task TheRealTask {
    do shit;
  }
}
This should be pretty self-explanatory.


And lastly, here's an example of way too many nestings and what you should NOT do:
Code: [Select]
function LolFunction {
  Lol1;
  Lol2;
  Lol3;

  function Lol1 {
    Rofl1;
    Rofl2;

    function Rofl2 {
       do shit;
    }
  }

  function Lol2 {
    Rofl1;
    Rofl2;
    Rofl3;

    function Rofl2 {
       do shit;
    }

    function Rofl3 {
       do shit;
    }
  }

  function Lol3 {
    Rofl3;
    Rofl4;

    function Rofl3 {
       do shit;
    }
  }

  function Rofl1 {
    do shit;
  }

  function Rofl4 {
    do shit;
  }
}
This is terrible. What the fuck is going on here? Well let's see. LolFunction calls Lol1, Lol2, and Lol3. Lol1 calls Rofl1 and Rofl2. Rofl1 is defined outside of Lol1, but Rofl2 is defined in it. In Lol2, it calls the Rofl1, Rofl2, and Rofl3. The Rofl1 is the same Rofl1 that Lol1 called, but the Rofl2 is different. Rofl3 in Lol2 is different from the Rofl3 in Lol3 as well. And in Lol3, Rofl4 is actually outside of it. Confusing enough? That's an extreme example of messy and unnecessary nesting. Don't do it.
« Last Edit: February 03, 2010, 06:51:20 AM by Blargel »
<WorkingKeine> when i get home i just go to the ps3 and beat people up in blazblue with a loli
<Azure> Keine: Danmakufu helper by day, violent loli by night.

Blargel

  • RAWR!
  • I'M AN ANGRY LOLI!
Re: [Tutorial] Advanced Danmakufu Scripting Techniques
« Reply #2 on: February 03, 2010, 03:46:00 AM »
Recursive Functions

What is it?
So you've made functions before. Hell, maybe you've made nested functions by now if you've been reading this tutorial in order. But have you made a function that calls itself (without having it blow up in your face and causing you to rage at Danmakufu)?

Recursive functions are functions that call themselves. They are special kinds of loops that many people find confusing, but they can become very powerful tools if used correctly. First of all, it may seem strange that a function can call itself. When you first think about it, it will sound like the function will infinitely call itself and enter an infinite loop. However, if you correctly construct the recursion, the function will call itself with different parameters every iteration until it reaches a certain configuration that makes it skip calling itself. The easiest way to think about this would be to imagine a function that has one parameter and that calls itself with 1 less of that parameter until it reaches 0 or less.

The benefit to recursive functions is that they are much more flexible and powerful than loops, whiles, or ascents. However, you must use caution when using them as they can easily cause infinite loops or exponential growth of tasks and bullets.


Why would you use this?
The use of recursion is usually not common at all, but sometimes, the easiest or simplest solution to a problem requires the use of one. For example, if you wanted to construct a chain of bullets that follows a leader, recursion is the easiest solution. You would just need create a task that manages the leader bullet and in that task, it would call a task that creates followers with the leader bullet's ID passed in as a parameter. The follower task would have the ID of the object it's supposed to follow and at the same time it would call itself using its own follower bullet's ID as the parameter. Of course without a second parameter to act as a counter and telling the task to cut off after a certain amount of follower have been made, we'd have an infinite loop.

That is just one of the situations that I've experienced that required recursive functions. As Naut described below, using a task to call itself at the very end is also a good use for particular effects.


Examples

First, a general example:
Code: [Select]
function RecursiveFunction(num) {
  if(num>0){
    RecursiveFunction(num-1);
  }
}
Obviously this code won't do anything, but notice how the function calls itself as many times are you tell it to with the parameter "num". It will not become an infinite loop and you can put anything you want before or after the if statement and it will run it that many times because it's being called that many times. This, of course, can be done with a loop quite easily, but this is just a demonstration of the recursion concept.


And here's an example of what I mentioned earlier with bullets forming a chain while following a leader bullet:
Code: [Select]
// This task is the one that calls the recursive task to create
// "num" amount of follower bullets.
task Leader(x, y, speed, angle, amplitude, frequency, graphic, delay, num){
  let obj = Obj_Create(OBJ_SHOT);
  Obj_SetPosition(obj, x, y);
  Obj_SetSpeed(obj, speed);
  Obj_SetAngle(obj, angle);
  ObjShot_SetGraphic(obj, graphic);
  ObjShot_SetDelay(obj, delay);
 
  Follower(obj, graphic, delay, num); //Calls the recursive task.
 
  // Some sort of weird behavior
  let counter = 0;
  while(!Obj_BeDeleted(obj)){
    Obj_SetAngle(obj, Obj_GetAngle(obj)+cos(counter)*amplitude);
    counter += frequency;
    yield ;
  }
}

// The recursive task.
task Follower(parentID, graphic, delay, num){
  let obj = Obj_Create(OBJ_SHOT);
  Obj_SetPosition(obj, Obj_GetX(parentID), Obj_GetY(parentID));
  Obj_SetSpeed(obj, 0);
  Obj_SetAngle(obj, Obj_GetAngle(parentID));
  ObjShot_SetGraphic(obj, graphic);
  ObjShot_SetDelay(obj, delay);

  // Makes it keep making more followers until num is 0.
  if(num>0){
    Follower(obj, graphic, delay, num-1);
  }
 
  while(!Obj_BeDeleted(obj) && !Obj_BeDeleted(parentID)){
    let x = Obj_GetX(parentID);
    let y = Obj_GetY(parentID);
    let angle = Obj_GetAngle(parentID);
    yield ;
    Obj_SetPosition(obj, x, y);
    Obj_SetAngle(obj, angle);
  }
  yield ;
  if(!Obj_BeDeleted(obj)){
    Obj_Delete(obj);
  }
}
The leader task is managing how the first bullet moves (in this case, with a strange formula that no one will really ever use) after it spawns the specified number of followers. The point of interest you should be looking at is the "num" parameter in the Follower task and the part that is commented with "Makes it keep making more followers until num is 0." You'll see that it passes the ID of the object shot that was just made into a new instance of the task, thereby making it the parent in the newer task while still being a follower to another bullet. The rest of the task is just storing and using previous values to do the "following" effect.


Here's a variation on recursion, though I've yet to have to do something like this in a real script:
Code: [Select]
function Upper(upperbound, lowerbound, value, increment){
  do shit with value;
  if(value<=upperbound){
    Lower(upperbound, lowerbound, value+increment, increment+1);
  }
}

function Lower(upperbound, lowerbound, value, increment){
  do some other shit with value;
  if(value>=lowerbound){
    Upper(upperbound, lowerbound, value-increment, increment+1);
  }
}
This is a variation where you have two functions going back and forth over and over until one of the conditions become false. This is even easier to screw up, but the option is there if you ever think you need it.


And of course here's an example of what not to do:
Code: [Select]
function InfiniteLoopLol {
  InfiniteLoopLol;
}
Sure it's calling itself, but when does it stop? This will generate an infinite loop.
« Last Edit: February 07, 2010, 08:35:36 AM by Blargel »
<WorkingKeine> when i get home i just go to the ps3 and beat people up in blazblue with a loli
<Azure> Keine: Danmakufu helper by day, violent loli by night.

Blargel

  • RAWR!
  • I'M AN ANGRY LOLI!
Re: [Tutorial] Advanced Danmakufu Scripting Techniques
« Reply #3 on: February 03, 2010, 03:46:19 AM »
Multi-Dimensional Arrays

What is it?
Hopefully, you've had some experience with arrays. In Danmakufu, they're a bit different than most other arrays in other languages, but the functionality is about the same: they store multiple values under the same name. For example, instead of having 10 different variables called Name1, Name2, Name3, Name4, etc., you can just have an array with a length of 10 storing all 10 names. However, what happens if, instead of putting numbers of strings in an array, you put more arrays?

This is what we call a multi-dimensional array. Arrays in arrays have their uses in organizing information more easily than single split arrays. However, they do tend to be more confusing to use than normal arrays. An array containing more arrays is called a two-dimensional array. If those arrays in turn also contain arrays, then it becomes three-dimensional. This pattern can go on indefinitely, meaning it is possible to make a 49205-dimensional array if you really really wanted to.


A Word of Warning
Do note that because Danmakufu is rather quirky, the construction and usage of multi-dimensional arrays may become tricky at times.

First of all, as you may remember, Danmakufu arrays can only hold one type of value: either numbers, strings, booleans, or arrays. If you try to mix these in an array, Danmakufu will raise an error. Well, arrays containing different types are also considered different types. If you try to put a string array with a number array in an another array, Danmakufu will raise an error. However, different length arrays are allowed to be in the same array.

Incidentally, you CAN change the type of value an array stores if you empty it first. Basically, any array that is just [] can become whatever you want it to be, even if it wasn't originally empty.

One last warning while dealing with multi-dimensional arrays in Danmakufu: Danmakufu can only change values one level deep. What I mean by this is that, while Danmakufu can retrieve values from, let's say, a two-dimensional array with let value = ArrayName[0][2]. It cannot set that value with Arrayname[0][2] = 5. The only way to change a single value would be to reconstruct the entire inner array. As you can probably imagine, the more dimensions the array has, the more of a pain it becomes to manage it.


Why would you use this?
In general, multi-dimensional arrays are used to keep track of information. Tasks are generally used to manage the information in the arrays though there are some exceptions.

One common use is to manage multiple instances of something. One instance of that something's attributes are stored as numbers in an array and every instance of that something is stored in a two-dimensional array. For example, if you wanted to draw a variable number of effects, but you wanted to use @DrawLoop instead of effect objects, you would need to store the positions of the effects. In that case, each array in the two-dimensional array would correspond to one of the effects. The first value in the inner array would be the x-coordinate and the second value would be the y-coordinate. For example, EffectArray[3][0] and EffectArray[3][1] would give the x- and y-coordinates for the 4th (0 is the first element in an array) effect. Of course you can store more information in that array such as alpha, color, blending styles, etc.

As a side note, many things you may not expect to be numbers are actually stored as numbers in Danmakufu. For example, whenever you call RED01 without a shot replace, it is really looking at a predefined variable that holds the number -65536. Other things like ADD, ALPHA, PRIMITIVE_TRIANGLESTRIP, NULL, YOUMU, and KOUMA -- basically anything in all caps and no quotes -- are also stored as numbers. This will make storing attributes as numbers much easier so that you don't get conflicting types in your arrays.


Examples

General example:
Code: [Select]
let 2DArray = [[0, 1, 2, 3], [2, -5, 39.20, 20], [305938.2834, -0.00002, 1.1111, RED05]];
let 3DArray = [[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]], [[13, 14, 15], [16, 17,18]]];

let BetterOrganize2DArray = [
  ["hi",  "hello"  ],
  ["bye", "goodbye"],
  ["yo",  "wassup" ]
];

let BetterOrganized3DArray = [
  [
    [10, 20,  30,  40 ],
    [50, 60,  70,  80 ],
    [90, 100, 110, 120]
  ],
  [
    [11, 21,  31,  41 ],
    [51, 61,  71,  81 ],
    [91, 101, 111, 121]
  ],
  [
    [15, 25,  35,  45 ],
    [55, 65,  75,  85 ],
    [95, 105, 115, 125]
  ]
];
There's many different ways to organize how your arrays look. the first two examples condense everything onto one line, which is fine if you don't really need to look at it. However, it's incredibly hard to tell which element of each array belongs to which indexes and therefore hard to get the correct value out when it is needed. The other two examples show how to organize it so you can more easily tell what each number corresponds to. It will take up more lines, but in the long run it'll be easier to read and maintain.


An example on changing values in a 2D array:
Code: [Select]
let Array = [
  [0, 1, 2, 3, 4],
  [1, 3, 5, 7, 9],
  [2, 4, 6, 8, 10]
];

let Variable = Array[1][3]; // Perfectly valid. Variable now holds the value of 7.
Array[1][3] = 8; // Not valid. Danmakufu cannot redefine a value more than 1 level deep.
Array[1] = [1, 3, 5, 8, 9]; // Valid and the only way to change a value more than 1 level deep.


An example to manage falling and rotating snowflakes in a 3D background (this example is rather long):
Code: [Select]
sub DrawSnowFlakes {
  SetTexture(imgSnowFlake);
  SetGraphicRect(0, 0, 64, 64);
  ascent(i in 0..length(SnowFlakeData)){
    SetGraphicAngle(SnowFlakeData[i][6], SnowFlakeData[i][7], SnowFlakeData[i][8]);
    DrawGraphic3D(SnowFlakeData[i][0], SnowFlakeData[i][1], SnowFlakeData[i][2]);
  }
}

task RunSnowFlakes(UpperXBound, LowerXBound, UpperYBound, LowerYBound, UpperZBound, LowerZBound){
  LoadGraphic(imgSnowFlake);  // Load the graphic.
  CreateSnowFlakes;           // Make it create snowflakes periodically.
  UpdateLogic;                // Make it update the snowflakes every frame.
 
  task CreateSnowFlakes {
    loop {
      loop(60){yield;}        // Wait 60 frames;
      SnowFlakeData = SnowFlakeData ~ [[ // Create a new snowflake
        rand(LowerXBound, UpperXBound),  // at a random x-pos between bounds
        UpperYBound,                     // at the top of the y bound
        rand(LowerZBound, UpperZBound),  // at a random z-pos between bounds
        rand(-1, 1),                     // with a random x-speed between -1 and 1
        -1,                              // with a y-speed of -1
        rand(-1, 1),                     // with a random z-speed between -1 and 1
        rand(0, 360),                    // at a random x-angle
        rand(0, 360),                    // at a random y-angle
        0,                               // at a z-angle of 0
        rand(-5, 5),                     // with a random x-rotation between -5 and 5
        rand(-5, 5),                     // with a random y-rotation between -5 and 5
        0                                // with no z-rotation
      ]];
    }
  }
 
  task UpdateLogic {
    loop {
      descent(i in 0..length(SnowFlakeData)){      // For every snowflake...
        if(                                        // If the snowflake's...
          SnowFlakeData[i][0] > UpperXBound ||     // x-pos is greater than the upper x bound
          SnowFlakeData[i][0] < LowerXBound ||     // or less than the lower x bound
          SnowFlakeData[i][1] > UpperYBound ||     // or its y-pos is greater than the upper y bound
          SnowFlakeData[i][1] < LowerYBound ||     // or less than the lower y bound
          SnowFlakeData[i][2] > UpperZBound ||     // or its z-pos is greater than the upper z bound
          SnowFlakeData[i][2] < LowerZBound        // or less than the lower z bound
        ){
          SnowFlakeData = erase(SnowFlakeData, i); // then delete the snowflake.
        }
        else {                                     // Otherwise...
          SnowFlakeData[i] = [
            SnowFlakeData[i][0] + SnowFlakeData[i][3],  // Add x-speed to x-pos
            SnowFlakeData[i][1] + SnowFlakeData[i][4],  // Add y-speed to y-pos
            SnowFlakeData[i][2] + SnowFlakeData[i][5],  // Add z-speed to z-pos
            SnowFlakeData[i][3],                        // x-speed stays the same
            SnowFlakeData[i][4],                        // y-speed stays the same
            SnowFlakeData[i][5],                        // z-speed stays the same
            SnowFlakeData[i][6] + SnowFlakeData[i][9],  // Add x-rotate to x-angle
            SnowFlakeData[i][7] + SnowFlakeData[i][10], // Add y-rotate to y-angle
            SnowFlakeData[i][8] + SnowFlakeData[i][11], // Add z-rotate to z-angle
            SnowFlakeData[i][9],                        // x-rotate stays the same
            SnowFlakeData[i][10],                       // y-rotate stays the same
            SnowFlakeData[i][11]                        // y-rotate stays the same
          ];
        }
      }
      yield;
    }
  }
}
The parts you should pay attention to is how I manage the different attributes in the array in the nested UpdateLogic task as well as how I use the attributes in the DrawSnowFlakes sub. If you're wondering why the creation logic has the z-angle and z-rotate set at 0, it's because I noticed that the flipping effect was extremely strange when done in Danmakufu. The logic would work though if you changed those values.


And an example of bad multi-dimensional arrays:
Code: [Select]
let MismatchedValueArray = [
  ["lol", "hi", "rawr"],
  [1,     2,    3    ]
];

let JustPlainWrongArray = [
  [1, 2, 3, "hi", "lol", [11, -49]],
  "like zomg",
  18,
  BLUE32
];
Both of these examples will raise an error in Danmakufu. Strangely, these are perfectly valid arrays in other langauges.
« Last Edit: February 09, 2010, 10:10:43 AM by Blargel »
<WorkingKeine> when i get home i just go to the ps3 and beat people up in blazblue with a loli
<Azure> Keine: Danmakufu helper by day, violent loli by night.

Blargel

  • RAWR!
  • I'M AN ANGRY LOLI!
Re: [Tutorial] Advanced Danmakufu Scripting Techniques
« Reply #4 on: February 03, 2010, 03:46:40 AM »
Libraries

What is it?
Do you ever find yourself using the same bit of code over and over? Maybe you made a function that you use in practically every script (the Wait function comes to mind here for you task
Spoiler:
ab
users out there). Sure you can just copy paste the function into every script you write, but what if you have something like fifty functions that you want access to in all your scripts? Are you going to copy paste a few hundred lines of code into everything you code?

Libraries are your answer here. They let you write a bunch of functions and define a bunch of variables in a separate file and then include all of them into any script with a single line, the oh-so-useful #include_function. Libraries are generally used to reduce the amount of code in a script and make it more readable for the larger projects.

Libraries are also a means to deliver a solution to a common problem that scripters will face. For example, recreating a cut-in from the later games such as MoF, SA, or UFO is not something everyone can do, but Helepolis has shared a library that you can use to easily imitate the effect by simply calling cutin and its parameters.


Why would you use this?
Most of the time, if you're just going to do a small script like a single attack or maybe a small plural, writing a library for it is going to be overkill. Once you start making larger scripts, however, you'll probably want to organize your functions and scripts better, and libraries are valuable tools to do this. Also, if you want to use other people's code to solve problems for you, the use of libraries will obviously be needed.


A Word of warning
You can easily make your script unplayable if you use #include_function incorrectly with absolute filepaths or messy relative filepaths. I highly suggest you take a look at this tutorial in order to ensure that you don't screw things up.


Examples

First, what a simple library will look like:
Code: [Select]
let GCSD = GetCurrentScriptDirectory;
let Images = [GCSD~"image1.png", GCSD~"image2.png", GCSD~"image3.png", GCSD~"image4.png", GCSD~"image5.png", GCSD~"image6.png"];

sub LoadImages {
  ascent(i in 0..length(Images)){
    LoadGraphic(Images[i]);
  }
}

sub DeleteImages {
  ascent(i in 0..length(Images)){
    DeleteGraphic(Images[i]);
  }
}
It's simply variables and functions. No script_enemy_main or @MainLoop or any of that. This is also a small example of a snippet you may want to include in a reusable library.


And here's how you add those functions into your script:
Code: [Select]
script_enemy_main {
  // Assuming that library.txt is the file that has the functions you want and
  // that it is in the same directory as the script doing the including.
  #include_function ".\library.txt"
  @Initialize {
    LoadImages;
    // and other stuff
  }
  @MainLoop {
    // stuff
  }
  @DrawLoop {
    // drawing stuff
  }
  @Finalize {
    DeleteImages;
  }
}
Again, be sure to take care when you use your #include_function so that it will still work correctly on other people's computers. *points to the paths and directories tutorial again*


And lastly, here are some examples of libraries that solve some common problems:
Expanded shotdata + shotreplace
Helepolis's Custom Cutin script
Blargel's Animation Handling Library
« Last Edit: February 09, 2010, 09:20:57 AM by Blargel »
<WorkingKeine> when i get home i just go to the ps3 and beat people up in blazblue with a loli
<Azure> Keine: Danmakufu helper by day, violent loli by night.

Blargel

  • RAWR!
  • I'M AN ANGRY LOLI!
Re: [Tutorial] Advanced Danmakufu Scripting Techniques
« Reply #5 on: February 03, 2010, 03:46:56 AM »
Extra Tips and Stuff

I'm a dumbass and forgot what I wanted to put in here. Maybe I'll remember.... someday....
« Last Edit: June 04, 2011, 07:09:31 AM by Blargel »
<WorkingKeine> when i get home i just go to the ps3 and beat people up in blazblue with a loli
<Azure> Keine: Danmakufu helper by day, violent loli by night.

Drake

  • *
Re: [Tutorial] Advanced Danmakufu Scripting Techniques
« Reply #6 on: February 03, 2010, 03:47:52 AM »
7 bucks says people who don't get intermediate concept will complain about how they can't do these properly

A Colorful Calculating Creative and Cuddly Crafty Callipygous Clever Commander
- original art by Aiけん | ウサホリ -

Re: [Tutorial] Advanced Danmakufu Scripting Techniques
« Reply #7 on: February 03, 2010, 03:50:03 AM »
7 bucks says people who don't get intermediate concept will complain about how they can't do these properly
Then we can tell em to go redo the intermediate tutorial until they get it :V

Re: [Tutorial] Advanced Danmakufu Scripting Techniques
« Reply #8 on: February 03, 2010, 04:27:05 AM »
Nobody reads tutorials nowadays. Thread might as well start with "Helepolis plox teech people how 2 do dis via vidya".

:V

Blargel

  • RAWR!
  • I'M AN ANGRY LOLI!
Re: [Tutorial] Advanced Danmakufu Scripting Techniques
« Reply #9 on: February 03, 2010, 04:32:03 AM »
Nobody reads tutorials nowadays. Thread might as well start with "Helepolis plox teech people how 2 do dis via vidya".

:V

I'd love to see someone explain this shit in video format. It'll be like a 20 minute video per section if you want to be as clear as possible.

7 bucks says people who don't get intermediate concept will complain about how they can't do these properly
If anyone posts anything that suggests that, then I will be seriously amazed that they actually read through and tried this stuff without the proper knowledge.

Also, first section up though the examples are pending. Might grab lunch before I get cracking on the examples.
« Last Edit: February 03, 2010, 06:45:20 AM by Blargel »
<WorkingKeine> when i get home i just go to the ps3 and beat people up in blazblue with a loli
<Azure> Keine: Danmakufu helper by day, violent loli by night.

Re: [Tutorial] Advanced Danmakufu Scripting Techniques
« Reply #10 on: February 03, 2010, 04:55:13 AM »
ilu lb

Blargel

  • RAWR!
  • I'M AN ANGRY LOLI!
Re: [Tutorial] Advanced Danmakufu Scripting Techniques
« Reply #11 on: February 03, 2010, 05:59:22 AM »
That's not a scripting technique. Perhaps you misunderstood the aim of the tutorial.
<WorkingKeine> when i get home i just go to the ps3 and beat people up in blazblue with a loli
<Azure> Keine: Danmakufu helper by day, violent loli by night.

Re: [Tutorial] Advanced Danmakufu Scripting Techniques
« Reply #12 on: February 03, 2010, 06:12:50 AM »
I.....guess I did, I will delete my post in a bit

Deleted
« Last Edit: February 03, 2010, 06:16:20 AM by Demonbman »

Helepolis

  • Charisma!
  • *
  • O-ojousama!?
Re: [Tutorial] Advanced Danmakufu Scripting Techniques
« Reply #13 on: February 03, 2010, 06:34:59 AM »
Forget the videos, some people simply do not read or listen at all. I don't know even if a video would be handy then.

PS good job. Once this is finished it will go in the FAQ.

Blargel

  • RAWR!
  • I'M AN ANGRY LOLI!
Re: [Tutorial] Advanced Danmakufu Scripting Techniques
« Reply #14 on: February 03, 2010, 08:15:42 AM »
Recursive Functions section up. I'm done for today. Feel free to pick out all my errors and yell at me. Or ask questions so I know which parts need more clarification.
<WorkingKeine> when i get home i just go to the ps3 and beat people up in blazblue with a loli
<Azure> Keine: Danmakufu helper by day, violent loli by night.

Re: [Tutorial] Advanced Danmakufu Scripting Techniques
« Reply #15 on: February 03, 2010, 08:22:53 AM »
Recursive functions were particularly useful when designing the chaining effect for Zengar A. The task calls itself everytime it successfully lands a hit on an enemy, causing the lasers to chain to another enemy. I have it limited so it only does it 3 times though, so you can't clean the map of enemies everytime a big wave comes down. The task also makes it so that it will not chain the previous enemy that it locked onto, if it's still alive.

Code: [Select]
task slash(x, y, ex, ey, state, dis, ang, identifier){
//build a fucking laser
let laser = Obj_Create(OBJ_LASER);
let dd = 0;
let enemy = 0;
Obj_SetPosition(laser, x, y);
Obj_SetSpeed(laser, 0);
Obj_SetAutoDelete(laser, false);
Obj_SetAngle(laser, ang);
ObjShot_SetGraphic(laser, 6);
ObjShot_SetDamage(laser, 0.25);
ObjLaser_SetSource(laser, false);
ObjLaser_SetWidth(laser, 10);
ObjLaser_SetLength(laser, 1);
dis/=5;
dd=dis;
loop(5){ //extend the tip of the laser over 5 frames, towards the enemy.
ObjLaser_SetLength(laser, dd);
dd+=dis;
yield;
}
loop(5){ //bring the base of the laser towards the tip over 5 frames, making it look like the laser jumped towards the enemy.
x = x + dis*cos(ang);
y = y + dis*sin(ang);
Obj_SetPosition(laser, x, y);
ObjLaser_SetLength(laser, dd);
dd-=dis;
yield;
}
x = ObjLaser_GetEndX(laser); //set the coordinates for later use, they will be where we spawn the next laser.
y = ObjLaser_GetEndY(laser);
loop(8){ //throw some effects around
Glitter(x, y, rand(0, 360));
}
splode(x, y); //another effect.
if(state>0){ //if the laser has not chained 3 times, continue.
let q = 0; //make a variable on the fly for limiting how many enemies we fire at.
ascent(i in EnumEnemyBegin..EnumEnemyEnd) {
if(q<1){ //if the previous variable we named is still below 1 (no enemies have been targeted), continue with the enemy enumeration.
enemy = EnumEnemyGetID(i);
ex = GetEnemyInfo(enemy, ENEMY_X); //set the coordinates of the enemy into variables so we know where we're going to aim.
ey = GetEnemyInfo(enemy, ENEMY_Y);
if(enemy!=identifier){ //if the enemy wasn't the last enemy we chained (isn't the enemy the current laser is killing).
if(((x - ex)^2 + (y - ey)^2)^0.5<100){ //Also, if the enemy is actually close to us (so we dont have lasers leaping accross the map even though there is a closer one).
q++; //indicate a suitable enemy has been found, so no other enemies will be attacked.
slash(x, y, ex, ey, state - 1, ((x - ex)^2 + (y - ey)^2)^0.5, atan2(ey - y, ex - x), enemy); //initialize the recurrance! state - 1 is to make sure that the lasers will stop once three have been chained together successfully, all other variables are the coordinates, angle, and id of the current enemy, so that the next slash may use them in it's favor.
PlaySE(GetCurrentScriptDirectory~"starburst.wav"); //play a sound, why not.
i = EnumEnemyEnd; //this didn't actually do anything, despite what I thought!
}
}
}
}
}
Obj_Delete(laser); //delete this laser
}

Pretty useful stuff.

EDIT:
[04:56:56]   <KimoKeine>   heh that's a nice example and use of recursion
[04:56:59]   <KimoKeine>   too bad it aint commented
« Last Edit: February 03, 2010, 08:35:42 AM by Naut »

Blargel

  • RAWR!
  • I'M AN ANGRY LOLI!
Re: [Tutorial] Advanced Danmakufu Scripting Techniques
« Reply #16 on: February 07, 2010, 08:33:30 AM »
God damnit I forgot about this over the weekend. I'll do more sections tomorrow. Added Multi-dimensional arrays and editing in something to point at Naut's post above as an example of recursion.
<WorkingKeine> when i get home i just go to the ps3 and beat people up in blazblue with a loli
<Azure> Keine: Danmakufu helper by day, violent loli by night.

Kylesky

  • *The Unknown*
  • Local Unbalanced Danmakufu Idiot Scripter
    • My useless youtube account... (will be useful in the future *I promise*)
Re: [Tutorial] Advanced Danmakufu Scripting Techniques
« Reply #17 on: February 07, 2010, 01:38:54 PM »
An example to manage falling and rotating snowflakes in a 3D background (this example is rather long):
Code: [Select]
Example pending. Feeling lazy. :V :V :V

This makes me think of my snowflakes :V but I think you're going to put something else of course...
Danmakufu Script Thread :V Latest Script: Intertwining Mechanical Intervention (temp name)

Yooooouuutuuuubeeee Channel Latest Video: Contest #8 Entry

Blargel

  • RAWR!
  • I'M AN ANGRY LOLI!
Re: [Tutorial] Advanced Danmakufu Scripting Techniques
« Reply #18 on: February 07, 2010, 03:26:15 PM »
This makes me think of my snowflakes :V but I think you're going to put something else of course...

I remembered the snowflake example that multiple people offered solutions for in the Q&A thread and thought it would be a good example of 2-D arrays. I had my own solution in there somewhere, but I'm not gonna dig through it to find faulty code I didn't test so I'm rewriting it someday. Don't worry, it'll be up before I do the next section.
<WorkingKeine> when i get home i just go to the ps3 and beat people up in blazblue with a loli
<Azure> Keine: Danmakufu helper by day, violent loli by night.

Azure Lazuline

  • Looooove!!
  • PM me for free huggles and love!
    • Entanma Project - indie game development
Re: [Tutorial] Advanced Danmakufu Scripting Techniques
« Reply #19 on: February 07, 2010, 04:44:19 PM »
And an example of bad multi-dimensional arrays:
Code: [Select]
let MismatchedLengthArray = [
  [1, 2, 3, 4, 5, 6, 7, 8],
  [1, 2, 3, 4, 5, 6, 7   ]
];
All three of these examples will raise an error in Danmakufu. Strangely, these are perfectly valid arrays in other langauges.

I think this is wrong. This doesn't raise an error, and it works perfectly:

Quote
let stageimages=[
["ender","ender2","ender_cutin","black","circuit","grid"],
["overload","overloadaura","overload_cutin","overloadbg1","overloadbg2","overloadbg3","empegh1","empegh2","empegh_cutin","empbg1","empnotes","staff"]
//etc
];

Blargel

  • RAWR!
  • I'M AN ANGRY LOLI!
Re: [Tutorial] Advanced Danmakufu Scripting Techniques
« Reply #20 on: February 08, 2010, 09:52:25 AM »
Wait what the hell? Time to test.

EDIT: Hell, you're right Azure. Thanks for pointing that out. I'm gonna go edit the section now.
EDIT2: Incidentally, this is valid too.
Code: [Select]
let a = [];
a = a ~ [["lol", "hi"]];
a = erase(a, 0);
a = a ~ [[0, 1]];
If you empty an array, you can change it's type. Guess I should add this too.
« Last Edit: February 08, 2010, 10:16:39 AM by Blargel »
<WorkingKeine> when i get home i just go to the ps3 and beat people up in blazblue with a loli
<Azure> Keine: Danmakufu helper by day, violent loli by night.

Areylie

  • Cirno's Sister
    • Lymia's Website
Re: [Tutorial] Advanced Danmakufu Scripting Techniques
« Reply #21 on: February 08, 2010, 11:17:41 PM »
I don't see the point of multi-dimensional arrays. Seems to be a waste of time, and extra coding effort with Damakufu's stupid handling of them.

Why not do something like this, which I used for a Conway's Game of Life script I once did:
Code: [Select]
let array = [];
let xs = 0;
let ys = 0;

function arrayinit(v,x,y){
  xs=x;ys=y;array=[];
  ascent(t in 0..x*y){
    array=array~[v];
  }
}
function getid(x,y){
  return x*xs+y;
}
function dosomething(x,y){
  CreateShot01(GetX,GetY,1,array[getid(x,y)],RED01,0);
}
[Geek code (3.12): GCS/H/M d+(-) s+:->--:- a--->? C+(++++)>$ UL++@ P+(++) L+++ E- W++ N o K? w-@ O- M- V? PS++@ PE>- Y+ PGP++ t- 5? X? R+@ tv- b+(++) DI D-- G? e>+++(++++) h! r++ x-]
[Furry code (1.3): FFm1r A !C D? H+++ M? P++++ R-- T W !Z Sf! RLCT a- cl+~++++>$ d--- e>+++ f? h* i++/+++ j+ p+ sf!]

Azure Lazuline

  • Looooove!!
  • PM me for free huggles and love!
    • Entanma Project - indie game development
Re: [Tutorial] Advanced Danmakufu Scripting Techniques
« Reply #22 on: February 08, 2010, 11:51:27 PM »
They both do the same thing, yes, but in most cases multi-dimensional is neater. It's really just preference though, and depending on what you're doing, one might be better than the other.

Re: [Tutorial] Advanced Danmakufu Scripting Techniques
« Reply #23 on: February 09, 2010, 03:46:08 AM »
Mention Notepad++ somewhere

Because it is amazing *-*

Blargel

  • RAWR!
  • I'M AN ANGRY LOLI!
Re: [Tutorial] Advanced Danmakufu Scripting Techniques
« Reply #24 on: February 09, 2010, 07:23:28 AM »
I don't see the point of multi-dimensional arrays. Seems to be a waste of time, and extra coding effort with Damakufu's stupid handling of them.

Why not do something like this, which I used for a Conway's Game of Life script I once did:
Code: [Select]
let array = [];
let xs = 0;
let ys = 0;

function arrayinit(v,x,y){
  xs=x;ys=y;array=[];
  ascent(t in 0..x*y){
    array=array~[v];
  }
}
function getid(x,y){
  return x*xs+y;
}
function dosomething(x,y){
  CreateShot01(GetX,GetY,1,array[getid(x,y)],RED01,0);
}
Mutli-dimensional arrays are usually better for stuff similar to the example I gave with the snowflakes, where there are many attributes that need to be kept track of and a variable amount of snowflakes. What would be required is a variable length array of arrays keeps track of each snowflake's attributes.

What you have there isn't easily understood, and like Azure pointed out earlier, Danmakufu multi-dimensional arrays can have different lengths per array. Your example there might be good for a fixed size array with x/y coordinates, but it isn't really flexible enough to do some of the more complicated things.

EDIT: Snowflake example added for the multi-dimensional array section. Added library section, though it's a bit short. May add to it in the future.
« Last Edit: February 09, 2010, 10:21:04 AM by Blargel »
<WorkingKeine> when i get home i just go to the ps3 and beat people up in blazblue with a loli
<Azure> Keine: Danmakufu helper by day, violent loli by night.