~Hakurei Shrine~ > Rika and Nitori's Garage Experiments

Danmakufu Intermediate Tutorial

Pages: << < (2/5) > >>

Naut:

How to make a boss fight!

A Plural file is a .txt file that allows you to string together multiple scripts to make a "boss fight". Essentially it operates by playing your scripts in the order you define in the file. It also manages how the boss' life bar will be broken up into sections. Plural files look like this:


--- Code: ---#TouhouDanmakufu[Plural]
#Title[ExRumia Boss Stage]
#Text[ExRumia boss fight, including regular attacks and spell-cards.]
#Image[.\img\ExRumia(?????u?~?b?h?i?C?g?????@???G?v).png]
#BackGround[Default]
#Player[FREE]
#ScriptVersion[2]

#ScriptPathData
#ScriptPath[.\ExRumia01.txt]
#ScriptPath[.\ExRumiaSpell01.txt]
#ScriptPath[.\ExRumiaSpell02.txt]
#ScriptNextStep
#ScriptPath[.\ExRumia02.txt]
#ScriptPath[.\ExRumiaSpell03.txt]
#ScriptPath[.\ExRumiaSpell05.txt]
#ScriptNextStep
#ScriptPath[.\ExRumiaSpell04.txt]

#EndScriptPathData
--- End code ---

Alrighty, the first little bit should be self-explanatory. Declare that this is a TouhouDanmakufu script, indicate that it is a plural script on the same line, set a title for the script, set some descriptive text, set an image to appear when you're selecting the script from Danmakufu's menu, set the background, set what player characters may be used, then indicate it's script version two.

Now, onto some of these new parameters.

#ScriptPathData starts the script path data (no wai). Be sure to declare this at the beginning of this string, it tells Danmakufu that the following is data for your plural script.

#ScriptPath[] tells Danmakufu to load and play the script you indicate in it's braces. It's relative to this file, so if your scripts are located in a subfolder, be sure to show that with [.\Sub Folder Name\script.txt]

#ScriptNextStep tells Danmakfu how to arange the life bars at the top of the screen. Everytime you call this, a new life bar is broken up and created, so all the scripts inbetween #ScriptNextStep will appear on the same lifebar. Test it out to see what I mean.

#EndScriptPathData does exactly what it says. Declare this at the end of your plural script.

And that's about it. Playing this script will tell Danmakufu to play all the scripts you've indicated inside this file, in decending order. So in the example, ExRumia01.txt will be played first, then when that script ends, ExRumiaSpell01.txt will be played, then ExRumiaSpell02.txt will be played, then a new lifebar will be created, then we'll move onto ExRumia02.txt, etc.

Naut:

How to make Familiars!

Best way to make familiars: Objects. Objects are made using tasks, which you should be familiar with after reading Iryan's task portion of this tutorial. The main difficulty you'll have with objects is getting the damn image to appear correctly, which can be accomplished by following Nuclear Cheese's Object Effect Drawing Tutorial. Or, if you prefer, you could skim through Stuffman's Player Script Tutorial, which explains how to make Sakuya's "options", which are basically familiars. Your best bet is to go through the player tutorial though, since it gives a basic outline of how to shoot with them as well (just remember you're not using "player shots").

The second way is the way I mostly use, and probably the easiest (though slightly messy). I traditionally make familiars using seperate enemy scripts, which are very easy to make and summon.

Just make a text file, and fill it with:

--- Code: ---script_enemy_main {
@Initialize{
    SetLife(10);
    etc.
 }
@MainLoop{
    [include no collion A or B so it can't be shot or collided with, include it if you want it to be shot]
    [attack functions, blah blah, like you would any script]
 }
@DrawLoop{
    [draw your familiar as you would any boss]
 }
@Finalize{
 }
}
--- End code ---

And to spawn it, anywhere in your main script you write:
CreateEnemyFromFile(script path of your familiar/enemy, x-coordinate, y-coordinate, velocity (can be defined in the script), angle (also can be defined in script), user-defined argument);

For the user defined argument, any value or text string you put there can be transfered to the enemy script by saying "GetArgument" in the enemy script. So if I said "2" as the last parameter of my CreateEnemyFromFile, in the enemy script I could say:
v = GetArgument;
And "v" would assume a value of 2 for the script.

Object are more flexable in terms of being able to share the same global variables with the main boss, as well as being able to get the coordinates of the boss, but Enemy Scripts are much easier to handle and are probably much easier to code advanced movements with. Choose whichever you fancy, but keep in mind that spawning familiars as enemies can have unexpected results (some player script homing shots may target it instead of the boss, etc).


Naut:

How to make Stages!

Start with your usual crap in any danmakufu script, except this time you're specifying that this is a stage script:


--- Code: ---#TouhouDanmakufu[Stage]
#Title[Stage script tutorial]
#Text[How to make stages in Danmakufu]
#Image[]
#Background[]
#BGM[]
#Player[FREE]
#ScriptVersion[2]

--- End code ---

You should know what everything here means, just make sure you've specified "Stage" after the first line.

Alrighty, onto how to script full-length stages. It's not very difficult actually, we just need to understand a few things beforehand. We actually don't even need to understand how tasks work, we just need to know how to use them. I won't bother explaining tasks and yield;, all of that is explained in Iryan's section of this tutorial. It's not necessary to know for this, but it certainly helps.

So the bulk of the code will go inside "script_stage_main{}". This is how it plays out:


--- Code: ---script_stage_main{

  @Initialize{

  }
 @MainLoop{
   yield;
  }
  @Background{

  }
  @Finalize{

  }
}
--- End code ---

Nothing unfamiliar. How this stage will play out will not be written in @MainLoop, but instead inside of a task. This way we can dictate exactly when things will occur by waiting a certain amount of frames before doing something else -- here's how we do that: We'll make a task just after script_stage_main and just before @Initialize, and call the name of that task in @initialize, like so:


--- Code: ---script_stage_main{

 task stage{

 }

  @Initialize{
 stage;
  }
 @MainLoop{
   yield;
  }
  @Background{

  }
  @Finalize{

  }
}
--- End code ---

This tells Danmakufu to run "stage" once at the very start of the script. So this way, we begin running our stage task as soon as the script starts. Let's declare some helper functions to make timing things in the stage task a little easier. We'll put these just before task stage{} so we can see them before anything else.


--- Code: ---script_stage_main{

 function Wait(let frames){
   loop(frames){yield;}
 }
 function WaitForZeroEnemy{
  while(GetEnemyNum != 0){yield;}
 }
 task stage{

 }

  @Initialize{
 stage;
  }
 @MainLoop{
   yield;
  }
  @Background{

  }
  @Finalize{

  }
}
--- End code ---

Alright, we've made two functions. The first is called Wait, and requires us to declare a parameter everytime we call it. What we've made it do is pause the script for however many frames we tell it to. So saying Wait(60); will tell Danmakufu to stop reading anything beyond it for 60 frames (1 second). The second function is called WaitForZeroEnemy, and doesn't require us to declare any parameters. It tells Danmakufu "when there are enemies on the screen (when the enemy count does not equal zero), wait". This is useful if you want the player to kill all the enemies you've spawned before continuing any further with your stage.

These functions will actually have use if we start putting things between them! So, let's get to spawning some enemies. The way we spawn an enemy is simple:

CreateEnemyFromFile(path of enemy script, x-coordinate, y-coordinate, velocity (if you didn't declare it in the script), angle (if you didn't declare it in the script), user-defined arguement);

These 6 parameters are simple: The first is the pathname of your enemy script, the second and third are the x and y coordinates of where you want your enemy to spawn, the fourth parameter is the velocity you want the enemy to have when you spawn it (you can set it to 0 and just define the velocity in the enemy script if you'd like), the fifth is the angle you want the enemy to move towards (again, can be defined in the script), and the last is a user-defined arguement. Basically, any value you want to pass on to the enemy script you put into the last paramet. You get this information by saying "GetArgument" in the enemy script. So, if I said 2 for the last parameter in CreateEnemyFromFile, in the enemy script I could say:

v = GetArgument;

And then the variable v would assume a value of 2 for that enemy script. Useful if you want to change bullet angles or something but still use the same enemy.

Alright, I've done alot of explaining about enemy scripts, yet you don't even know how to create them. Well, good knews! They're exactly  the same as what you've been creating all along, except with even less information! In an enemy script, you can neglect anything beginning with "#" (like #TouhouDanmakufu, #Title, etc), and just get straight to script_enemy_main. In that, it's just the same shit, but a different pile. You include your @Initialize, @MainLoop, @DrawLoop and @Finalize just like a boss script. Load graphics, spawn bullets, set life, all of it you do exactly the same. For enemies, you won't want them to have too much health, so you can kill them quickly. Typically have them spawn a really basic flurry of bullets, and just continue off in one direction (they will be destroyed upon leaving the game screen).

So all that information is nice, but how do we throw it all together into a stage? Let's take a look:


--- Code: ---#TouhouDanmakufu[Stage]
#Title[Stage script tutorial]
#Text[How to make stages in Danmakufu]
#Image[]
#Background[]
#BGM[]
#Player[FREE]
#ScriptVersion[2]

script_stage_main{

 function Wait(let frames){
   loop(frames){yield;}
 }
 function WaitForZeroEnemy{
  while(GetEnemyNum != 0){yield;}
 }
 task stage{
  Wait(120);
  CreateEnemyFromFile(GetCurrentScriptDirectory~"enemy.txt", GetCenterX, GetCenterY, 0, 0, 0);
  CreateEnemyFromFile(GetCurrentScriptDirectory~"enemy.txt", GetCenterX, GetCenterY, 0, 0, 0);
  WaitForZeroEnemy;
  CreateEnemyFromFile(GetCurrentScriptDirectory~"enemy2.txt", GetCenterX, GetCenterY, 0, 0, 0);
  WaitForZeroEnemy;
  CreateEnemyBossFromFile(GetCurrentScriptDirectory~"cirno.txt", 0, 0, 0, 0, 0);
  WaitForZeroEnemy;
  Wait(60);
  Clear;
 }

  @Initialize{
 stage;
  }
 @MainLoop{
   yield;
  }
  @Background{

  }
  @Finalize{

  }
}
--- End code ---

Let's go through how this stage will play out. First, the stage will wait two seconds before anything happens. Then, it will spawn two enemies at the center of the screen whose behavior I've defined in "enemy.txt", which is located in the same script directory as my stage file. The stage script will then wait until the player kills those two enemies before spawning a different enemy, "enemy2.txt". The script will again wait for the player to kill the enemy, then it will create a boss. The CreateEnemyBossFromFile function is exactly the same as CreateEnemyFromFile function, except you can summon plural files as well, and the enemy will be treated as a boss (will have an enemy marker, can use spellcards, etc). The stage script will then wait for you to defeat the boss (can be a multi-spellcard plural file), then end the stage with the "Clear" function.

Make sure you include "yield;" in your @MainLoop for the stage file, this allows all the other yield; commands to work correctly.

And that's it. This stage is incredibly small, since most stages spawn hundreds of different kinds of enemies, which require seperate enmy files or really complex GetArgument statements. The coding is long and tiring, but it looks freakin' sweet when you finish one off. Good luck!

Naut:

How to make Event scripts!

Alrighty, for event scripts, you're gonna have to go beyond "script_enemy_main{}" inside of a spellcard and write the following:


--- Code: ---script_event name{


@Initialize{
  [Load all your graphics and foolishness here]
 }

@MainLoop{
 SetChar(LEFT, [player graphic]);                 //Set the player's character on the left side, with the graphic you've told it to display.
 SetGraphicRect(LEFT, [left-side], [top-side], [right-side], [bottom-side]);           //The region you're displaying of the graphic for the player character, just like SetGraphicRect.
 MoveChar(LEFT, BACK)                             //Move the player's character into the background, to show she is not speaking.
 SetChar(RIGHT, [enemy's grahic]);                //Set the boss' picture on the right side of the screen.
 SetGraphicRect(RIGHT, 0, 0, 200, 350);           //Set the boundry of the picture you want displayed.
 MoveChar(RIGHT, FRONT);                          //Move the boss' image to the front to show that she is speaking.
 TextOutA("The text you want the character to speak goes here");           //Self explanatory. Danmakufu will not pass this function until a certain amount of time has passed, or the player clicks the shot button.
 MoveChar(RIGHT, BACK):                           //Move the boss to the background, then...
 MoveChar(LEFT, FRONT);                           //Move the player forward, to show that she will now speak.
 TextOutA("More words here");                     //What the player will be speaking.
 End;           //This ends the event.
 }

@Finalize{
  [delete all your graphics here]
 }
}
--- End code ---

Keep in mind that anything after "//" in Danmakufu is a comment and will not be processed, so I can explain the functions as they appear in the code.

To initialize the event, call in your script:

CreateEventFromScript("name");

Which will tell Danmakufu to look in the current file for an event script called "name", then run it.

For your TextOutA commands, there are two special character combinations that will tell Danmakufu to do different things. The first is /n, which will tell Danmakufu to write everything proceeding it on a new line. The second is /c[COLOUR], which tells Danmakufu to write everything proceeding it in the designated colour. Colours available are the standard bullet colours: RED, GREEN, BLUE, YELLOW, PURPLE, AQUA, and WHITE.

A few other text options are available, including TextOutB, which has two parameters:

TextOutB(time text is displayed in frames, "text you want to be displayed");

As you could guess, this is the same thing as TextOutA except is displayed for a certain amount of frames. Think about the midboss text for the Extra stage of any Touhou game. The text can't be skipped and is displayed for a certain amount of time, which you designate in the first parameter. As per TextOutA, /n and /c[COLOUR] are available.

Select is another text function for event scripts, and is used for choosing two different options. Think stage five Imperishable Night (go versus either Eirin or Kaguya by choosing path A or path B). There are two parameters for Select:

Select("Option number 1", "Option number 2");

The function will then output a value based on which option the player chose, either 1 or 2. So to use this function in conjunction with different things happening, you could say:

if(Select("Stage 1", "Stage 2")==1){
   [code for "Stage 1"]
   }else{
   [code for "Stage 2"]
}

And that's about all you need to know for event scripts. Happy talkin'!

Naut:

So, anything else need explaining?

Pages: << < (2/5) > >>

Go to full version