~Hakurei Shrine~ > Rika and Nitori's Garage Experiments
I just started using Danmakufu,
Naut:
You mean spawn bullets from another bullet? The AddShot function is what you're looking for, and that's explained rather nicely in Blargel's Basics tutorial, in case you haven't read it.
Don't feel bad for asking questions, it gives us something to do (plus, I rather enjoy solving these problems). I'm on this forum more often than I'll admit, so ask away!
Cabblecorento:
One question:
How would I make something like a stage?
Naut:
Don't.
Here's why: You're new to Danmakufu, and stages take ages to code and yield very little for the amount of work you'll end up putting into it. It's a really big turn-off from Danmakufu coding, so I would recommend getting comfortable with just plain Spell-cards and Plural Scripts before attempting stage files.
...
...
However, if you're absolutely certain that you want to make a stage, here we go:
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 can be found here. 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 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!
Cabblecorento:
Thanks.
I'm going to get started on a 1 minute stage with a 1 attack mid-boss and a 3-attack boss.
Now I need to get images working. I need to look at Nuclear Cheese's tutorial again.
Naut:
Good luck, it takes freakin' months. [/hyperbole]
Edit: Actually, no. Not hyperbole.
[tear]