Sparen's 0.12m to ph3 Transition Guide

From Danmakufu Wiki
Jump to: navigation, search

Please note: This guide is no longer being maintained, and may therefore be out of date. For up to date (hopefully) resources, consult Sparen's Danmakufu ph3 Tutorials, as they are updated on a (hopefully) more frequent basis.

Welcome to my 0.12m to ph3 transition guide. It is meant to be a resource for people transitioning from 0.12m to ph3 or a guide for intermediate ph3 users who are trying to go from bosses to either stages or full games, and assumes intermediate-level knowledge of 0.12m. Those new to Danmakufu should consult Helepolis's video tutorials or some other source before reading this guide, as most if not all of this might be confusing. I tend to use quite a bit of CS (Computer Science) jargon, and this can be quite dense.

Please note that at the bottom, there is a basic function library that may be quite useful for populating your own library with basic functions.

I will not go over function equivalents for functions that have simply be renamed. For removed functions or highly changed functions, I will note major differences. For new functions, I will explain their function to the best of my ability if their usage is not obvious (like with GetMouseX).

Note to those reading this tutorial in order to learn ph3: If there are errors or things are not clear, please notify me so that I can clarify them. If a section is labelled as Intermediate or Advanced, that is simply a label, and feel free to read those sections if you want to.

Note to editors: insert a space in order to prevent # from turning into a list and to activate code blocks.

So? Let's begin.

Basics

First thing's first. Almost every non-math function has been renamed in ph3. Also, rand_int does not exist. Use round(rand(int a, int b)). ph3 also has a large number of new functions, which you should check out and look into. Note that a few functions have slightly different uses.

Please use the following link for function equivalents: 0.12m/ph3 Function Equivalents

Please use the following link for a list of ph3 functions: Functions (ph3)

Secondly, it is extremely important to note that ph3 is heavily object-dependent. If you struggled with objects in 0.12m, I highly suggest mastering things such as object bullets before hand.

Thirdly, please note that ph3 uses #ScriptVersion[3] instead of #ScriptVersion[2].

Fourth, in 0.12m, you could use \ in order to change directories instead of /. That's not possible in ph3. For moving back a directory, use ./../

Also, when you include functions, the paths in the included file are based on the position of the included file rather than the file including it. (?)

Fifth, in 0.12m, the origin (0,0) was located at the top left of the window. In ph3, it is the top left of the playing field.

However, if you are new, don't let ph3 scare you. It's MUCH more flexible than 0.12m, mainly due to the fact that you need to make more things yourself. You can center the screen, shake the screen, go fullscreen, remove the STG_Frame, etc. It's much more streamlined in regards to CommonData usage and making entire games as well.

Let me give examples of awesome ph3 things.

  • In MPP, the DDC invert is used and 3D backgrounds are suddenly much more streamlined
  • In Miransu's LOCAA 3 Contest Entry, there is fullscreen, a time system, and a system for lives and bombs, as well as all kinds of other cool things.
  • In YKK, you can choose your bosses like in GFW
  • In TalosMistake's Halloween Contest Entry, he flips the screen and controls like in DDC Stage 5
  • In Darkness1's Halloween Contest Entry, he controls the player when they enter a certain moving radius
  • In Thaws's RaNGE 10 Contest Entry, he allows the player to flip the bullets on the screen

GTbot has also made a function for splitting and rearranging the screen. Also, a large number of scripts feature difficulty/effect level choosing inside the script.

Much more is possible in ph3. There's a HRtP-style game already available, there's a working Aya photo player, and all kinds of other cool things are possible.

The Danmakufu Header and Framework

Header

Below is a sample Danmakufu header. Please note that not every possible item may be included here. Check the ExRumia and sample scripts that come with ph3 for more examples.

 #TouhouDanmakufu[Plural]
 #Title["Title goes here"]
 #Text["Line one[r]Line 2"]
 #Background["./Background.dnh"]//assumes Background.dnh in same directory as script
 #Player["./player/KogasaOkuu/KogasaOkuuA.txt", "./player/KogasaOkuu/KogasaOkuuB.txt"]//Example only
 #ScriptVersion[3] 
 #System["./system/System.txt"]//ph3 will call default system if this system does not exist

So I will begin with changes. Firstly, all stings go in quotations. Also, instead of hitting enter/return to make a new line in the headers, you must use [r]. Note that text does NOT wrap.

Note that #Background is no longer used to create a scrolling background using an image. Now you use it and include a file path to a background SCRIPT, which will be explained later on. #System also links to a system script that is executed as soon as a script starts. Many people link to the default system in their scripts, so it is highly recommended that you do not fool around with the file path to the default system. If you would like your scripts to have a custom system, it is better to include that system within the script rather than overwrite the default system.

Also note that #BGM and #Image also exist. The former... really shouldn't be used since there are far better options in ph3, though it does the same thing as in 0.12m. The latter performs the same function as in 0.12m.

Please use the following for the default settings.

 #Player[DEFAULT]
 #Background["script/default_system/Default_Background_IceMountain.txt"]
 #System["./script/default_system/Default_System.txt"]
 #include"script/default_system/Default_ShotConst.txt"

Note that you no longer have #include_function and #include_script. It's all #include now.

Framework

Now, the basic framework. Remember script_enemy_main? That's gone now. Same with @DrawLoop, @Background, and @DrawBottomObject. Yes. @DrawLoop is gone. You will use tasks to simulate that. Loading textures/graphics is optional in ph3, although it prevents lag when the media in question are loaded (if you do not load them, they will be loaded temporarily when you first use them, and if the file is large, that may cause unwanted lag when the graphic is first used). Note that most people only use @Finalize if they load textures or other graphics in @Initialize. The standard is to control closing the script, death explosions, etc. in a separate TFinalize task.

For TFinalize in Single scripts, you call it in @Initialize and start it with the following:

       while(ObjEnemy_GetInfo(objBoss,INFO_LIFE)>0){yield;}
       yield;

This code assumes that the variable name for your registered OBJ_ENEMY_BOSS is objBoss. This will tell your script to not execute the rest of the code until the boss has no HP. After this, you should do something similar to the following.

       DeleteShotAll(TYPE_ALL,TYPE_ITEM);
       Obj_Delete(objBoss);
       DeleteShotAll(TYPE_ALL,TYPE_IMMEDIATE);
       CloseScript(GetOwnScriptID());

Some people use SetAutoDeleteObject(true), which will automatically delete all of the sound, text, and render/prim/mesh/etc objects created in the single. However, this prevents you from spawning items in the Single, so it is also possible to manually delete all of your graphics when the boss's life is <= 0. This assumes you are using tasks for your graphics. Please note that most graphics are now Render or Primitive objects.

Reset2DCamera is recommended only if you use screen-shaking or have a static background that's not supposed to shift positions. If you have a background that doesn't need to fit into certain dimensions, don't use it.

For TDrawLoop, you should do something similar to this if you are including the task in your single and aren't controlling the animation from an included script.:

   task TDrawLoop{
     let BossImage = GetCurrentScriptDirectory~"./../img/RaikoSprite.png"; //Define path
     ObjPrim_SetTexture(objBoss, BossImage);
     ObjRender_SetScaleXYZ(objBoss, 1, 1, 1);
     ObjSprite2D_SetSourceRect(objBoss, 0, 0, 80, 96);
     ObjSprite2D_SetDestCenter(objBoss);
   }

This task assumes that you delete your boss object in TFinalize. You do not need to manually delete this graphic because it is linked to the boss obj and will be deleted automatically when the boss obj is deleted. There will be more on the difference between Move and Render later on in this guide. Please consult the Table of Contents for faster navigation.

Here is the same code but with the animation (not a complete animation; just a basic one)

   task TDrawLoop{
     let BossImage = GetCurrentScriptDirectory~"./../img/RaikoSprite.png"; //Define path
     let animframe = 1;
     let bossAngle = ObjMove_GetAngle(objBoss);
     let bossSpeed = ObjMove_GetSpeed(objBoss);
     ObjPrim_SetTexture(objBoss, BossImage);
     ObjRender_SetScaleXYZ(objBoss, 1, 1, 1);
     ObjSprite2D_SetSourceRect(objBoss, 0, 0, 80, 96);
     ObjSprite2D_SetDestCenter(objBoss);
     while(!Obj_IsDeleted(objBoss)){
       bossAngle = ObjMove_GetAngle(objBoss);
       bossSpeed = ObjMove_GetSpeed(objBoss);
       ObjSprite2D_SetSourceRect(objBoss, 0+floor(animframe/7)*80, 0, 80+floor(animframe/7)*80, 96);
       if(bossSpeed>0){
         ObjSprite2D_SetSourceRect(objBoss, 0, 96, 80, 192);//I am only using one graphic for movement
       }
       ObjSprite2D_SetDestCenter(objBoss);
       animframe++;
       if(animframe == 28){animframe = 0;} 
       //Reset animframe after 28 frames. Change number depending on the number of sprites
       yield;
     }
   }

Note that if you want an animation, you must use a loop, preferably a while loop. Do not forget to yield;.

@Event

ph3 also brings in @Loading and @Event. In @Loading, you can mass-load resources so that they aren't loaded when they are first used (which may cause unwanted momentary lag). Use this so that you can load resources in advance and use them in other scripts. Note that you can also load resources in @Initialize or in actuality, anywhere you want to load them, and that loading things in the #System file will allow all files to use them. In @Event, you control a large number of things, such as spell card bonuses, timers, boss HP, etc.

For @Event in a Spell, you would use the following:

   @Event{
       alternative(GetEventType())
       case(EV_REQUEST_LIFE){SetScriptResult(3000);}
       case(EV_REQUEST_TIMER){SetScriptResult(60);}
       case(EV_REQUEST_SPELL_SCORE){SetScriptResult(1000000);}
   } 

Note that EV_REQUEST_### is a constant and you need to spell it correctly. (Wise words from Ozzy: "Isn't that true for just about everything? XD")

For @Event in player scripts, please go to the player script section.

There is also EV_USER, and things such as EV_USER+1, etc are possible since it is a constant. You can use NotifyEvent(GetOwnScriptID(), EV_USER+1, args), NotifyEventAll(EV_USER+1, args), or variations on them to perform tasks such as item spawning.

For examples on types of constants, look at this link: How to Write Scripts#Events (@Event)

For a tutorial on EV_USER, please check out Miransu's EV USER Guide.

An example of how to use NotifyEventAll is located below.

NotifyEventAll (Advanced)

Note that since NotifyEventAll is usually used for communication across scripts and may be confusing to understand, it is best to tackle NotifyEventAll after you have had practice with ph3 and are comfortable with the basics. If you plan to make a full game or a stage with enemy death effects and point items or even boss spell capture sound effects, however, NorifyEventAll is your friend.

The following code is located in the stage's @Event

   alternative(GetEventType())
   case(EV_USER_STAGE+666){//Boss Spell Capture
      	  let capture = GetCurrentScriptDirectory~"./sound/capture.wav";
         LoadSound(capture);
         PlaySE(capture);
   }
   case(EV_PAUSE_ENTER){ObjSound_Stop(bgm);}
   case(EV_PAUSE_LEAVE){ObjSound_Play(bgm);}

The EV_PAUSE_ENTER and EV_PAUSE_LEAVE are built-in constants. If you do not tell the BGM to stop, it won't stop when you enter the pause menu.

Inside a single is the following:

   if(ObjEnemyBossScene_GetInfo(objScene, INFO_PLAYER_SHOOTDOWN_COUNT)+
         ObjEnemyBossScene_GetInfo(objScene, INFO_PLAYER_SPELL_COUNT) == 0){
     AddScore(ObjEnemyBossScene_GetInfo(objScene,INFO_SPELL_SCORE));
     NotifyEventAll(EV_USER_STAGE+666, 0); 
   }

This code is noteworthy for two reasons; Firstly, it awards the SpellCard Bonus (which is NOT DONE BY DEFAULT in ph3). Secondly, it calls NotifyEventAll. This call from the plural is sent to every open script being used by Danmakufu. It will eventually hit the stage, and the EV_USER_STAGE+666 will trigger the Spell Capture sound effect to be loaded and played. Since the single will cut off the SFX when it is closed, this is a technique that allows for things such as explosion graphics, sound effects, and more to be executed in a stage, where they won't get cut off. It can be used for virtually everything that requires immediate action by another script, and is extremely useful when circumventing the auto deletion of objects. The 0 passed to the @Event is an arbitrary value in this case, but you can pass an array into the NotifyEventAll and use let arg = GetEventArgument(0); to access the argument passed along.

EV_USER_STAGE and NotifyEventAll is the preferred method of spawning items, since SetAutoDeleteObject deletes items when they are spawned in a single.

Plurals

Plurals are completely different from 0.12m. There is no more ugly making a stage in order for a plural to have a background. Plurals are customizable and flexible, and can be used in a large variety of situations.

Below is a sample plural script.

 #TouhouDanmakufu[Plural]
 #Title["Sample Plural Script"]
 #Text["Line 1[r]Line 2"]
 #Background["./Background.dnh"]
 #Player["./player/KogasaOkuu/KogasaOkuuA.txt", "./player/KogasaOkuu/KogasaOkuuB.txt"]
 #ScriptVersion[3]
 #System["./system/System.txt"]  //system is a folder in the same folder as the plural.
 
 #include"./system/Default_Effect.Txt" //Default_Effect.Txt is ph3's default effect library, moved into the system folder
 #include"./FxnList.txt" //FxnList.txt is a function library
 
 @Initialize{
     TPlural;
     SetPlayerLife(10); 
     /*ph3 does not have continues by default. 
       You must make your own continue system or give a large number of lives if your script is difficult.*/
 }
 
 @MainLoop{
     yield;
 }
 
 task TPlural{
     let dir=GetCurrentScriptDirectory();
     let obj=ObjEnemyBossScene_Create(); //Plurals are made from Boss Scene objects
     Sound(GetCurrentScriptDirectory~"music/boss.ogg"); 
     //Sound is a user-made function to control the music. You can find the function under Sound Objects.
     ObjEnemyBossScene_Add(obj,0,dir~"./script/SXBNS1N.txt"); //This is how you add a single to the plural. 
     ObjEnemyBossScene_Add(obj,0,dir~"./script/001RaNiSP1N.txt"); //The 0 or 1 is the lifebar to add the attack to.
     ObjEnemyBossScene_Add(obj,1,dir~"./script/SXBNS2N.txt");
     ObjEnemyBossScene_Add(obj,1,dir~"./script/003RaNiSP2N.txt");
     ObjEnemyBossScene_LoadInThread(obj); //You must load the plural inside the thread/process
     ObjEnemyBossScene_Regist(obj); //You must register the Boss Scene object
     while(!Obj_IsDeleted(obj)){ //Until the plural has been cleared, the TPlural yields.
         yield;
     }   
     CloseScript(GetOwnScriptID()); //Close the plural.
 }

Please note that if you define any CommonData outside of the @Initialize, etc, they will be overridden by then next single script added to the plural. So for example, if I SetCommonData("Attackno", 1); in SXBNS1N and SetCommonData("Attackno", 2); in SXBNS2N, the Log window will state that the value of Attackno is 2 during SXBNS1N despite the value being set to 1. To avoid this, set common data values in @Initialize, control them from outside the Single scripts, or use separate Boss Scenes. This is because Boss Scenes read through the singles as they are added. This is also the reason why plurals can detect errors within singles (outside of code blocks such as @Initialize) if Boss Scenes are used.

Note that since #Background and #System exist, you can call your own entire system and run it in the Plural without having to make a stage.

Packages and .def files (Intermediate)

Packages are a brand new type of script introduced in ph3. Here, you can create menus with some ease, have player select menus, and bypass the start screen using a .def file. Note that packages are still a royal pain to learn, although once you get the hang of text objects and virtual key input, it becomes much more simple.

You start a package with something similar to the following:

 #TouhouDanmakufu[Package]  
 #Title["Digital Earth: Conquest"]
 #Text["Digital Earth: Conquest[r]Package"]

Do note that you CANNOT call #System, #Background, and other such things in a Package script. In the @Initialize of a package, you may want to load any config AreaCommonData from save files or create the CommonData Areas if they do not exist. For example:

 if(!LoadCommonDataAreaA2("Config", GetCurrentScriptDirectory~"data/Config.dat")) { CreateCommonDataArea("Config"); }

Also, it is highly recommended that you install all necessary fonts used in the package in the @Initialize. Your @MainLoop should have a yield; in it.

Most of your menu panes will be made up text objects and scene tasks. It is recommended that you have a separate file for the Replay Select scene since it can get extremely complicated. It is best to build off of the existing package default script in the ExRumia folder that comes with ph3. It is also recommended that you yield the start of the scene tasks so that players trying to go back one menu don't accidentally go back multiple menus.

It is also recommended that you define constants for the indexes of each menu item so that things are easier to read. Make sure you have a good understanding of 2D Sprite Objects, Text Objects, and AreaCommonData before you try to create your first package, as it will make your time with packages much easier.

Def Files (Intermediate)

In the GetModuleDirectory (the directory with th_dnh.exe), there should be a th_dnh.def file.

 package.script.main = script/ExRumia/ExRumia_Package_Main.txt
 window.title = DIGITAL EARTH: CONQUEST
 screen.width=640

The following are examples of commands used in the .def file. The package.script.main makes the selected package path load on startup of th_dnh.exe. The window.title is the title of the window (notably, you do not need "" to mark these things as strings, and you don't need semicolons. Comments are still done with //). Screen width and height can also be edited this way.

Boss and Enemy Objects

Did you ever want to make two enemies at once? Well... ph3 has that covered. You use one boss enemy and one enemy enemy.

Single Enemy

At the start of a Single (before @Initialize), do the following:

   let objBoss;
   let objScene=GetEnemyBossSceneObjectID();

Then in @Initialize...

   	objBoss = ObjEnemy_Create(OBJ_ENEMY_BOSS);
   	ObjEnemy_Regist(objBoss);
   	SetDamageRateAtFrame(objBoss,80,50,90);
   	ObjMove_SetDestAtFrame(objBoss, GetCenterX, GetCenterY-120, 60); 

In ph3, you must register every enemy. SetDamageRateAtFrame is basically SetDamageRate and SetInvincibility in one neat package. GetCenterX, GetCenterY, and SetDamageRateAtFrame are available at the bottom of this tutorial in the Helpful Functions section.

       ObjEnemy_SetIntersectionCircleToShot(objBoss, ObjMove_GetX(objBoss), ObjMove_GetY(objBoss), 32); 
       ObjEnemy_SetIntersectionCircleToPlayer(objBoss, ObjMove_GetX(objBoss), ObjMove_GetY(objBoss), 24);

These functions are the hitbox functions for ph3. Yes: they are longer. But at least you know which one is for the player and which one is for shots. You can spawn multiple hitboxes, so you can dump one onto the objEnemy (this assumes that they share a lifebar).

Enemies are controlled using ObjMove, although in this example, ObjRender is used for the enemy instead of ObjMove (both are possible, ObjMove is preferred).

Control the boss's position using ObjMove commands and shoot shots using ObjMove commands. Basically everything else is the same as 0.12m.

Multiple Enemies (Intermediate)

At the start of a Single (before @Initialize), do the following:

   let objBoss;
   let objEnemy;
   let objScene=GetEnemyBossSceneObjectID();

Then in @Initialize...

   	objBoss = ObjEnemy_Create(OBJ_ENEMY_BOSS);
   	ObjEnemy_Regist(objBoss);
   	SetDamageRateAtFrame(objBoss,80,50,90);
   	ObjMove_SetDestAtFrame(objBoss, GetCenterX, GetCenterY-120, 60); 
   	objEnemy = ObjEnemy_Create(OBJ_ENEMY);
   	ObjEnemy_Regist(objEnemy);
   	SetDamageRateAtFrame(objEnemy,80,50,90);
   	ObjRender_SetPosition(objEnemy, Obj_GetValueD(objScene,"NitoriX", 0), Obj_GetValueD(objScene,"NitoriY", 0), 1);
   	//Above retrieves Nitori's X and Y
   	ObjMove_SetDestAtFrame(objEnemy, GetCenterX-120, GetCenterY-120, 60);

Note that the objBoss is the primary enemy and that its ID comes first when you get the array of all enemies. If you only want one enemy, ignore all of the code referring to objEnemy.

As for Obj_GetValue, you can set a value in @Finalize, similar to CommonData. It's basically CommonData unique to a specific object, in this case, the objScene, since it contains all of the single scripts and can therefore pass on values.

       ObjEnemy_SetIntersectionCircleToShot(objBoss, ObjMove_GetX(objEnemy), ObjMove_GetY(objEnemy), 32); 
       ObjEnemy_SetIntersectionCircleToPlayer(objBoss, ObjMove_GetX(objEnemy), ObjMove_GetY(objEnemy), 24);

This is code for a shared lifebar with the objBoss

To save the position of the objEnemy for the next single, use Obj_SetValue(objScene, "ObjPos", [ObjMove_GetX(objEnemy), ObjMove_GetY(objEnemy)]); before you delete the objEnemy in TFinalize and in the @Initialize of the next single, ObjMove_SetX(objEnemy, Obj_GetValue(objScene, "ObjPos", [0,0])[0]); ObjMove_SetY(objEnemy, Obj_GetValue(objScene, "ObjPos", [0,0])[1]); If you don't like arrays, you can set an X value and a Y value and call those separately.

Stage Enemies (Intermediate)

In ph3, there are no functions for creating stage enemies. However, that does not mean that you cannot create your own.

You must make your own functions. You can LoadScript on a complete @Initialize, etc script in the stage that contains an enemy (and that file will contain tasks that identify enemies), include tasks that spawn and control enemies, etc.

The method is completely up to the scripter. For those who want a single file to contain all enemies, Ozzy's Artifact 1 Contest entry contains a relatively simple (but extremely specific) way of controlling enemies. MPP and Fairies of Sorcery have pretty indecipherable enemy code.

It is highly recommended that you do not start on stage enemies until you are comfortable with using either LoadScript or #includes and nested tasks. Please do note that if you LoadUserShotData in #System, all enemies formed in the stage with that #System will be able to access that shot data.

The easiest way to spawn enemies is to #include files with tasks that spawn enemies and set their life, movement, etc. Use ObjMove for the enemy itself and ObjRender for the graphics.

Shots and Lasers: Changes

ph3 brings major changes to shots and lasers. Before, you had no control over them unless you made object shots or object lasers. Now, all of them are objects, and are fully customizable.

Here are the shot functions: Shot Functions

Firstly, each shot/laser creating function returns their object ID. To utilize this, do the following:

 let objShot = CreateShotA1(ObjMove_GetX(objBoss), ObjMove_GetY(objBoss), 3, GetAngleToPlayer(objBoss), 45, 10); 

Note a few things:

Secondly, 45 is a graphic ID from a shot sheet. Do note that all shot constants are different and that ph3's default bullets are way nicer than 0.12m's. Shotsheets are covered later in this guide.

Next, notice GetAngleToPlayer(objBoss). GetAngleToPlayer functions the same way as in 0.12m, except now you need to give it the object ID of the enemy.

Finally, I am storing the ID of the shot in the variable objShot. You can dump this ID into an array or simply act on it right there and then.

Remember CreateShotA and CreateShotA_XY? In ph3, you can use ObjMove_AddPattern functions to make the bullets do things. Since the normal shot creation functions return the bullet ID, you can set speed, angular velocity, etc using the ID. As for CreateShotOA1 and CreateShotOB1, they create a bullet that will spawn on the coordinates of the given object id. The bullet and its arguments act the same as CreateShotA1 in 0.12m, but this is NOT the same.

An important note about ph3: You will use ObjMove functions to control all of these bullets. Also, CreateStraightLaserA1 (the new CreateLaserA and CreateLaserB), has delete time built into it. The delete time will start ticking AFTER the laser has been formed. The laser graphic will always form, although it will have no hitbox if delete time is set to 0.

Delay Lasers

To make delay lasers, create a task like this:

   task DelayLaser(x, y, ang, l, w, dt, graph, delay){
       let objlaser = CreateStraightLaserA1(x, y, ang, l, w, dt, graph, delay);
       wait(delay-1);//So that the graphic never enlarges. 
       Obj_Delete(objlaser);
   }

Remember that the wider the delay laser, the more likely it will render correctly. Using non-straight laser graphics may result in the delay laser not showing completely. Remember to test your code and make sure that there are no cheap shots with your delay lasers, because hidden delay lasers can be problematic for gameplay.

Curvy Lasers

For curvy lasers, you must use ObjMove_SetAngularVelocity to set its curve. Annoying but easily done. You have a lot more control over them in ph3, and lasers look MUCH better. Note that pre23 allows you to use non-ADD lasers.

Curvy lasers in ph3 have transparent tips. From ph3 [.1 pre3] and onwards, use ObjCrLaser_SetTipDecrement to set the transparency of laser tips.

ph3 also features ways for you to retrieve shot IDs from a certain area. Note that player shot IDs can be retrieved by GetShotIdInCircleA2. Finally, note that you can retrieve shot information using GetShotDataInfoA1.

Shot Object Functions

Move and Render Objects

The two main types of objects you will deal with use Move and Render functions. All bullets, enemies, and the like use ObjMove to control them. Graphics use ObjRender. However, there are a number of things that are extremely important.

Please reference the following! [Object Hierarchy Diagram]

  • All enemies will use ObjMove for pretty much everything. However, the object can and should also use ObjRender commands for their graphics. Use ObjRender to set and control their graphic, and use ObjMove to control the position and other attributes of the object itself.
  • There are a lot of ObjRender functions; Spend time looking at the accessors (functions that get information, like ObjMove_GetX) and mutators (functions that change data, like ObjRender_SetAlpha, and see which ones fit your needs.
  • ph3 allows for ADD_ARGB, INVERT, SUBTRACT, and MULTIPLY blends, and you can now change hue, saturation, and value. Keep these things in mind when you use Render Objects. Most of your @DrawLoop and @Background code in 0.12m will be altered into ObjRender form in ph3.

ph3 also introduces 2D sprites, 2D sprite lists, 3D sprites, meshes, and the like. Most of you ObjEffect functions are now ObjPrim commands (see later), but you should probably use 2D sprites to take care of those. That way, you can remove all of the confusing vertices and maintain all of the flexibility since ObjRender commands are valuable. Note, however, that your graphics in ph3 should be twice as big, then scaled down. Otherwise, blur will occur at the edges, unlike in 0.12m where the image would be laid out perfectly, pixel for pixel. 3D sprites shouldn't be used unless you specifically need 3D sprites.

Note that in ph3, there are more than 9 render priorities. In fact, there are infinite render priorities. Functions such as Obj_SetRenderPriority allow you to set the render priority of an object. This gives much more flexibility with graphics and layering. Some important things to notice are as follows:

  • The location of the origin changes from the top left of the STG frame to the top left of the window when you use a render priority of 80 (.8) or above.
  • By default, the STG_Frame is drawn at the very bottom. If you want a different render priority, change this in #System=>InitFrame().
  • By default, the Score, Graze, etc. text and the numbers associated with them are drawn with a render priority of 1 (0.01). If you want a different render priority, change this in #System
  • By default, the boss lifebar is drawn with a render priority of 70 (0.7). If you want a different render priority, change this in #System=>TBossLife.
  • By default, the boss spell timer is drawn with a render priority of 75 (0.75). If you want a different render priority, change this in #System=>TBossTimer.
  • By default, the spell card bonus display is drawn with a render priority of 60 (0.6). If you want a different render priority, change this in #System=>TGainSpell(score).
  • By default, the FPS will be drawn at the very top. If you want a different render priority, change this in #System=>TCurrentFPS() and #System=>TReplayFPS().
  • By default, the player will be drawn with a render priority of 30 (0.3). You can change this in your player script
  • By default, bullets will be drawn with a render priority of 50 (0.5). You can change this if you make shot objects.
  • By default, items will be drawn with a render priority of 60 (0.6). You can change this if you make a custom item script.
  • If you have two graphics on the same layer, the one created later will appear on top of the one created earlier
  • Note that there may be discrepancies here; Avoid render priorities of 20 and below.

Primitive Objects (Advanced)

Primitive Objects are the Effect Objects of ph3. However, with the introduction of 2D sprites, most of the uses of primitive objects suddenly aren't there anymore. If you are aiming to do crazy graphical things involving things with multiple vertices (such as circular life bars) or other things involving selecting portions of graphics for individual manipulation, you will have to use primitive object functions.

The basic function to create a primitive object is ObjPrim_Create, which you will use to create your 2D Sprites, 3D Sprites, Sprite Lists, and Primitive Objects. See Render Object functions for more information on 2D sprites.

There are three primitive types: TriangleList, TriangleStrip, and TriangleFan. The official description of how to use them can be found [here]. If you are trying to implement these and are having trouble, those on the Q&A thread may be able to help you.

Most of the primitive object commands function the same way as in 0.12m. Note that ObjPrim_SetVertexPosition, the new XY vertex function, now requires a Z coordinate. Place 0 or 1 if you don't need it. Also, note that color and alpha can no longer be controlled by the same function, and that you must use two separate functions.

Here are the primitive functions: Primitive Object Functions

Sound Objects

Sound in ph3 is much more customizable. You can now loop music! The recommended file type for bgm is .ogg, and the recommended file type for sfx is .wav. Please don't use .wav for bgm. Wavs are clunky, have horrifically large file sizes, and in ph3, .ogg is just way better. Sound effects are still best as .wav though.

Use .ogg for looping. mp3 works fine in ph3.

You have multiple types of sound. You can do the normal LoadSound and play it, or you can make Sound Objects, which are much more customizable and support looping. #BGM is still useful, and PlaySE, and PlayBGM are still great options if you decide to not use the clunky but customizable Sound Objects. Either way, you MUST LoadSound in order to use it in ph3.

 function Sound(path){ //For music. 
     let obj = ObjSound_Create;
     ObjSound_Load(obj,path);
     ObjSound_SetSoundDivision(obj, SOUND_BGM);
     ObjSound_SetRestartEnable(obj, true);
     ObjSound_SetLoopEnable(obj, true);
     ObjSound_SetLoopTime(obj, 0,300);
     ObjSound_Play(obj);
     ObjSound_SetVolumeRate(obj,95);
     return obj;
 }

This code uses a some of the many functions available for sound. You can also set pan and other things using the built in functions.

Please look here for the general audio functions. Audio Functions

Please look here for a full list of Sound Object functions. Sound Object Functions

Text Objects

Did you hate 0.12m's ugly DrawText? Well say bye to that ugly horror!

ph3 brings in Text Objects. They are MUCH more customizable, not ugly at all, and can support a number of different fonts! YES! In ph3, you can use your own fonts (if they are supported)!

 task CutinText(strText){
     let objtext = ObjText_Create();
     ObjText_SetText(objtext, strText);
     Obj_SetRenderPriority(objtext, 0.74);//Render above cutin
     ObjText_SetFontSize(objtext, 16);
     ObjText_SetFontColorTop(objtext, 255, 64, 64);
     ObjText_SetFontColorBottom(objtext, 255, 64, 64);
     ObjText_SetFontBold(objtext, true);
     ObjText_SetFontBorderType(objtext, BORDER_FULL);
     ObjText_SetFontBorderColor(objtext, 0, 200, 200);
     ObjText_SetFontBorderWidth(objtext, 1);
     ObjRender_SetPosition(objtext, GetStgFrameLeft+5, GetStgFrameHeight-30, 0);
     loop(300){yield;}
     Obj_Delete(objtext);
 }

Above is the text I used for my spell cut in. You create a text object, set its render priority, set its font size, set font color, etc. The border is a full border around it, as seen in #Text when selecting players. Note that they are Render objects, and note that there are many more functions that I did not use for this particular task.

Now you can ditch 0.12m DrawText and use this far-superior one. ^_^

(Do note that text can be a little bit of a pain to position)

Linked is a list of Text Object functions. Text Object Functions

NOTE: Scripts made in ph3 [.0 pre23] and before may have text compatibility issues with ph3 [.1 pre2] and later. It is highly recommended that you install your own font so that you know if your words are being chopped in half and thrown onto different lines. Please note that InstallFont returns a boolean value; Make sure your font is actually being installed successfully.

SetHorizontalAlignment

In this section, I will discuss usage of ObjText_SetHorizontalAlignment(obj, char), where char is one of the following:

  • ALIGNMENT_CENTER
  • ALIGNMENT_RIGHT
  • ALIGNMENT_LEFT

Firstly, you MUST use ObjText_SetMaxWidth. Not using this will send your text flying off the screen.

For an example, I will be centering the Spellcard Bonus text. task TGainSpell(score){

       let objText = ObjText_Create();
       ObjText_SetText(objText, "ExAttack Bonus!");
       //Code removed
       ObjText_SetHorizontalAlignment(objText, ALIGNMENT_CENTER);
       ObjText_SetMaxWidth(objText, 240);
       ObjRender_SetX(objText, 194-120);//Minus half the max width. 194 = 388/2 (388 is GetStgFrameWidth w/ r.p.<80)
       ObjRender_SetY(objText, 98);
       //etc.

Look at ObjRender_SetX. Here, I have 194 pixels, which is 388 (the width of the playing area), divided by 2. This is the axis around which I want to center the text. Now, I subtract it by 120 pixels, which is half the maximum width of the max width. This shifts the text to the center. If it had been ObjRender_SetX(objText, 194), The area Danmakufu would have centered the text in would have been 194 pixels (the center of the playing field) + 240 pixels (maximum width).

NOTE: Different fonts may not fit within the MaxWidth, pushing letters to the next line. Render priorities above 80 do not follow the 388 pixel rule, and you must use GetStgFrameLeft+388 for the x axis and GetStgFrameTop+your desired y coordinate for the equivalent to occur.

If I wished to push the text to the left or right, I would follow a similar procedure.

Fonts

Here I will discuss installing fonts.

Here is some sample code that creates a text object. It will be referenced later.

   let objGraze = ObjText_Create();
   ObjText_SetText(objGraze, "Graze");
   ObjText_SetFontSize(objGraze,20);
   ObjText_SetFontType(objGraze,"Fairview Regular");
   ObjText_SetFontBold(objGraze, true);
   //etc

Let us consider 4 possibilities. Parentheses used to denote order of operations.

  • 1: The font Fairview Regular is not installed on the user's computer AND (has not been installed by InstallFont OR InstallFont failed)
  • 2: The font Fairview Regular has been installed on the user's computer AND (has not been installed by InstallFont OR InstallFont failed)
  • 3: The font Fairview Regular is not installed on the user's computer AND (has been installed by InstallFont OR InstallFont succeeded)
  • 4: The font Fairview Regular has been installed on the user's computer AND (has been installed by InstallFont OR InstallFont succeeded)

In the first scenario, no luck. The font will display in MS Gothic or in something weird, like Times New Roman or Arial. It will usually default to MS Gothic or whatever your computer's default fonts are.

In the second and third scenarios, the font has been installed somewhere and therefore, Danmakufu can use it without any issues.

In the last scenario, it works. Simple.

Now, how to install fonts? Use InstallFont, which returns a boolean value.

   InstallFont(GetCurrentScriptDirectory~"./../Helvetica.dfont");
   InstallFont(GetCurrentScriptDirectory~"./../Andale Mono.ttf");
   InstallFont(GetCurrentScriptDirectory~"./../Revue.ttf");
   InstallFont(GetCurrentScriptDirectory~"./../Russell Square Regular.ttf");
   InstallFont(GetCurrentScriptDirectory~"./../Fairview_Regular.otf");
   //if(!test){RaiseError("Font Install Failed");} //Test code

This code is located at the bottom of @Initialize in my system file, called by #System. It is also good to place your font installation code in the @Initialize of your package as well if you are using a package.

When installing, you give InstallFont the path to the font, NOT it's name. Now, let us utilize the test code.

   InstallFont(GetCurrentScriptDirectory~"./../Russell Square Regular.ttf");
   let test = InstallFont(GetCurrentScriptDirectory~"./../Fairview_Regular.otf");
   if(!test){RaiseError("Font Install Failed");} //Test code

This code tests whether Fairview Regular is being successfully installed. If it is NOT successfully installed, Danmakufu will raise an error popup. If it is successfully installed, the script will continue as normal. Run the test for each and every font to make sure there are no errors. Of course, when all are succeeded, comment out the test code. It's best to tell those who download your script to install the fonts anyways.

As for using the fonts, we will reference the code posted above:

   let objGraze = ObjText_Create();
   ObjText_SetText(objGraze, "Graze");
   ObjText_SetFontSize(objGraze,20);
   ObjText_SetFontType(objGraze,"Fairview Regular");
   ObjText_SetFontBold(objGraze, true);
   //etc

There is a call to use the font Fairview Regular here. When using SetFontType, address the font by its name, NOT its path. If the font has not been installed/incorrect font name, the default font or last used font or some other font will be used instead. Obviously, this is not desirable.

Note that Danmakufu does not (for obvious reason) support every font type. However, TrueTypeFonts and OpenTypeFonts (.ttf and .otf respectively) should work, and as they are the most common, it should not be too problematic to use them.

That's fonts in a nutshell. Make sure to test it yourself.

Path, Time, and Debug Functions

Regarding file paths, when you #include a file in a script, all pathing in the included file assumes that the directory in which the included file is located in is GetCurrentScriptDirectory, unlike in 0.12m where the path was based on the file which called #include.

Most of the pathing files are self explanatory and are clearly explained in the function list.

Linked is a list of Path Functions. Path Functions

Time functions are pretty self-explanatory as well. Important are GetCurrentFPS and GetReplayFPS

Some people have decided that making a game where the stage changes depending on the player's computer's time is possible, and in fact, it actually is. Just note that text functions can get pretty clunky.

Linked is a list of Time Functions. Time Functions

There are only 2 debug functions (use the Debug Window for all practical debug purposes).

WriteLog and RaiseError are the only functions.

WriteLog is your standard print function. RaiseError functions similar to custom errors in 0.12m, and is excellent for testing if something is actually working.

Linked is a list of Debug Functions. Debug Functions

Mesh Objects (Advanced)

In ph3, you can now use 3D meshes! However, the only supported file extensions are .mqo (Metasequoia) and .elem (Elfreina), and both of those softwares are in Japanese. I highly recommend not attempting to do Mesh Objects until you have either mastered how primitive objects work or learned how to do 3D modeling.

ObjMesh_Create creates the mesh object, and you can set color, alpha, animation, and position via 2D coordinates.

Unfortunately, I have never used Mesh Objects nor do I know how to create the textures or models required. Therefore, I highly recommend using an alternate source.

Linked is a list of Mesh Functions. Mesh Object Functions

Render Targets, Shader Objects and High Level Shader Language (Advanced)

Ever wanted a burn effect that doesn't kill your processor? Vanishing objects without killing your processor? Strange effects that do wonderful things?

Well, Danmakufu always had Render Targets, which allowed you to save snapshots of the screen, turn the image into primitive objects immediately, and do awesome things with them. These tend to be extremely hard to use, and require mastery of primitive objects in order to be used to their full potential.

Ph3 [.1 pre4] introduced Shader Objects and danmakufu compatibility with High Level Shader Language (HLSL).

Please refer to PhantomSong's post here for more information: Link

I for one cannot actually explain render targets or HLSL. Therefore, you will need to turn to an alternate source for information on these.

Linked is a list of Shader Object Functions. Shader Object Functions

File Objects (Advanced)

File objects are some of the most complicated things to use in ph3. You can create either binary or text files. However, I have used neither, although there are some scripters that have taken advantage of these. I highly recommend using an alternate source in order to understand how File Objects work and can be used.

In text files, you can get the line count, text, and other things. You can split the lines similar to in Replay Comments and add/clear lines.

In binary files, you can do a large number of tasks. However, I have no clue how to use any of the functions.

Linked is a list of File Object Functions. File Object Functions

Linked is a list of Text File Object Functions. Text File Object Functions

Linked is a list of Binary File Object Functions. Binary File Object Functions

Data Types

  • NOTE: These are from my AP Computer Science notes on Java 7; some of these may behave differently in Danmakufu

Boolean: True or false.

Byte: 8 bit data type that holds numbers from -128 (-2^7) to 127 (2^7-1)

Short: 16 bit data type that holds numbers from -2^15 to 2^15-1

Integer: 32 but data type that holds numbers from -2^31 to 2^31-1

Long: 64 bit data type that holds numbers from -2^63 to 2^63-1. This is more digits than you will ever need.

Float: 32 bit data type that holds floating point numbers

Double: 64 bit data type that holds floating point numbers to more decimal points than you will ever need.

Character: 16 bit data type that holds one letter/number/symbol. Capital and lowercase numbers are different characters. These CANNOT be read using Binary File functions.

String: An array of characters (put simply).

Common Data (Intermediate)

In 0.12m, Common Data was a precious means of storing data for use over a number of scripts. However, it didn't always... work correctly. Well... in ph3, it works. Very well.

Common Data are organized into Area Common Data, which contain Common Data inside them. Unless you specify which 'area' a Common Data is located in, it will be in the "" Area Common Data. This is the first slot in the Common Data debug window (the blank one), and you can access the values from there.

Note that GetCommonData now acts the same as GetCommonDataDefault did in 0.12m. The main usage of Area Common Data is for organizational purposes and for saving information to files. Most of the CommonData functions are self explanatory.

In ph3, loading and saving Common Data to files is much simpler. Just use one of the save functions and it will save to a specified file (A2) or just save into a data directory (A1). You can also save specified Area Common Data to replay files.

Note that if you want to save CommonData to a replay, doing so may desync or otherwise damage the replay. PLEASE use Replay Comments. For more information on replay comments, it is recommend that you visit a Q&A thread such as the one in Rika and Nitori's Garage Experiments. Basically, you save CommonData as strings linked by / or some other barrier separating them and when you load the replay comment, you separate the individual CommonData using the /. In order to understand replay comments, you must have a working knowledge of splitting strings and converting data from one form to another, such as integers to strings and back. Please do not try this unless you are either making a large project or know enough about splitting strings to effectively utilize Replay Comments, as it will cause unnecessary stress and confusion.

Note that in a BossScene, if CommonData are defined outside of a block such as @Initialize, they will be overridden by the value presented in the next Single added to the BossScene. Therefore, it is recommended that you set your Single script specific CommonData in @Initialize. Note that in 0.12m, the default value of a CommonData is the string "0". In ph3, you are required to define your own default. Make sure that the type of your default is the same as the type you will be using with it. For example, the default for a String CommonData would be "", etc.

Note that Obj_SetValue can act similarly to CommonData. However, you are required to pass a string through Obj_SetValue.

For CommonData functions, click this link: Common Data Functions

#System (Intermediate)

ph3 made functions to be called by all scripts much easier. #System is a brand new glorious thing in ph3. All tasks called here will be automatically run by EVERY script that uses that System file. No more changing things in multiple places; just store all of your life counters, Difficulty images, and STG_Frame animations in #System. Also, you can install fonts here, and this is a great place to set the pause and end scene paths, as well as loading shot data for stages.

Do all of the task calling, font installation, and scene pathing in @Initialize. @MainLoop should contain a yield and maybe a counting variable if you really need it. There is also an @Event here that controls magic circles and spell capture bonuses, as well as things that you set it up to run. It is highly recommended that you install a font and use custom fonts for all text objects simply so that all users will have a consistent font (since default fonts may have issues depending on your operating system settings). Make sure that the font actually installs!

#Background and Camera Functions

Unfortunately, after a year of ph3, I still don't understand what I'm doing when making 3D backgrounds. However, control 2D backgrounds as you would other 2D sprite objects. This section will most likely need another completely separate tutorial, as it is an extensive subject covering mesh, 3D Sprite, and 2D sprites in conjunction with 2D and 3D camera functions as well as pathing, boolean manipulation, alpha and rgb manipulation, and more.

Script and System Functions

Script and System functions are the functions that you use while scripting and have no clue how they work. They're just there, integral parts of your script that you copy and paste as you script. However, they do have a purpose, and there are some important things to note.

LoadScript and LoadScriptInThread load a script (with its own @Initialize, etc) in either the current or a different thread. This is not an #include, and all global variables are initialized in the loaded script. You then Start it and Close it when it is done running. IsCloseScript is very useful for pausing a stage script while a loaded plural is running.

SetScriptArgument and GetScriptArgument can be used to set and get values passed to a script, just like Obj_SetValue and the enemy script arguments of 0.12m.

There are many script and system functions, and it is really up to you to find one that works for what you are trying to accomplish. Just note:

Point items DO NOT AUTOMATICALLY get recorded as being added. You must manually AddPoint either in your player script or in a custom item script.

And remember: when spawning bullets, the left side of the playing screen is 0, not GetStgFrameLeft. Same goes for the top of the screen.

For Script functions, click this link: Script Functions

For System functions, click this link: System Functions

ShotSheets

In ph3, you are no longer limited to 255 shots! Hooray! With pre23, you can also ADD_ARGB your bullets, so no more black backgrounds needed! Shotsheets are a massive step up from in 0.12m, and you are no longer restricted by size of the sheet. Also, did I mention that ph3's default bullets are much prettier than the 0.12m ones? Well, they are.

For information on default graphics, see the following: How to Write Scripts#Bullet Graphics (Default Shotsheet)

If you want to load a shot constant sheet such as the default shot sheet, please use this:

 #include"script/default_system/Default_ShotConst.txt"

This particular code uses absolute pathing to the default shot sheet. It is highly recommended that you store a shot sheet within your script and link to that just in case someone has removed or renamed their default_system folder. By #including a constant sheet, you can address bullets as DS_BALL_S_RED and the like instead of using numerical IDs for the bullets.

Making Shotsheets (Advanced)

To make shot sheets, you do the following:

 #UserShotData
 
 shot_image = "<insert path here>.png"
 delay_rect = (L, T, R, B)

L, T, R, and B are the coordinates stating the rect. delay_rect is not required.

Here is a sample shot definition. (taken from How To Write Scripts Tutorial)

 ShotData {
   id = 1
   rect = (0,0,32,32)	
   render = ALPHA
   delay_color = (255,128,0)
   angular_velocity = 3
   fixed_angle = true
   collision = 5	
 }

id must be unique for every bullet.

rect states the rect coordinates of the bullet graphic, Left Top Right Bottom.

render can be ALPHA, ADD, ADD_ARGB, and even MULTIPLY and SUBTRACT in pre23. You will probably stick with ALPHA, ADD (for black backgrounds) and ADD_ARGB.

delay_color is (255, 255, 255) by default. The arguments are Red, Green, and Blue

angular_velocity is 0 by default. It is basically how fast the graphic spins.

fixed_angle states whether the graphic will be static. In other words, if you set it to true for a heart bullet, no matter which direction it is going in, it will always be a heart with the point facing downwards.

collision is the hitbox radius. The default value is based on the size of the bullet graphic. It is recommend that you set this for bullets with a large aura.

You may put these in any order and may put them on the same line (recommended) like this:

 ShotData{ id=1 rect=(0,0,16,14) render=ALPHA angular_velocity = 0 delay_color= (128,128,128) } //Gray
 ShotData{ id=2 rect=(16,0,32,14) render=ALPHA angular_velocity = 0 delay_color= (255,64,64) } //Dark Red
 ShotData{ id=3 rect=(32,0,48,14) render=ALPHA angular_velocity = 0 delay_color= (255,64,64) } //Red

In order to animate bullets, you just set multiple rects like this: (taken from How To Write Scripts Tutorial)

 ShotData{ id=371 render=ADD angular_velocity = 0 delay_color= (255,64,64) 
   AnimationData{
     animation_data=(4,256,324,288,356) 
     animation_data=(4,288,324,320,356) 	
     animation_data=(4,320,324,352,356) 	
     animation_data=(4,352,324,384,356) 	
   }
 }  

The first argument in animation_data is how many frames the bullet will stay in that graphic for.

Note the following closely if you are making a shot sheet: If you want your bullets to spin, you MUST have an odd width for the bullet and an odd height for the bullet or the bullet will shake when spinning. There is no solution except to change the rects on the shot sheet, changing hitbox location, or to change the graphic itself (i.e. change a bullet with an even width/height to an odd width/height, etc.), which will change the graphic itself.

The reason for this may lie in a center pixel being needed to rotate graphics in Danmakufu. You can go for either the slightly-off hitbox or the different graphic. First is gameplay, latter is aesthetics. If you're using ZUN's bullets, note that you WILL have to choose between these two.

For more information, see the following: How to Write Scripts#Adding Custom Bullet Graphics (Custom Shotsheets)

Players (Intermediate)

Unfortunately, this will require a separate tutorial, as creating a player from scratch is not easy. My [Artifact 2 Marisa Player on Bulletforge] is free to download, and feel free to browse the code and try to understand how it works. Note that it is based off of GTbot's players. I highly suggest looking at the Random Player Generator, which has what is probably the most simple yet versatile code available for study next to the default player.

Keyboard and Mouse Input

First and foremost, here is the list of Input Functions: Input Functions

On this page, there is a list of all keys and states, as well as all virtual keys (keys that Danmakufu has special uses for, such as the arroyo keys, the shot button Z, etc. Note that USER1 is C and the USER2 is V by default.

The virtual keys function the same way as in 0.12m. Do note that you can make your own virtual keys using AddVirtualKey. Note that this will require usage of AddReplayTargetVirtualKey.

GetKeyState and GetMouseState use KEY_FREE, KEY_PUSH, KEY_HOLD, and KEY_PULL.

Note that mouse commands cannot currently be used in replays. If you use the mouse in replays, it may later break them since Danmakufu is reading the mouse rather than the location of the mouse from replay data.

That's all for user input via keyboard and mouse.

Misc and Unintended Outcomes

Notes on Tasking and Bullet Delay

This section is meant to specifically warn people who emulate my style of calling non-looping tasks for the sole purpose of looping attacks. First and foremost...

   task TFinalize{
       while(ObjEnemy_GetInfo(objBoss,INFO_LIFE)>0){yield;}
       yield;
       DeleteShotAll(TYPE_ALL,TYPE_ITEM);
       Obj_Delete(objBoss);
       DeleteShotAll(TYPE_ALL,TYPE_IMMEDIATE);
       CloseScript(GetOwnScriptID());
   }

Note the presence of the yield; Depending on your scripting style, if you do not use that yield (or multiple yields)... unwanted things can happen, such as effects not deleting and bullets spawning from (0,0). It is recommended that you yield; more than one frame if you don't want to immediately rush into the next single or if you spawn bullets with more delay than 0 frames. However, if your scripting style us set up so that all tasks end and no bullets can be fired, then you may want to have no yields at all after the while loop has ended.

Basically, in ph3, tasks continue going until they finish or until the script terminates. If you spawn bullets after DeleteShotAll(TYPE_ALL,TYPE_IMMEDIATE); OR have bullets with delay, then they will not be deleted and will exist in the next script. The same thing goes for graphics. If you don't set SetAutoDeleteObject(true);, the graphics will continue to exist until deleted manually. This means that you must terminate the effect tasks manually. Make sure that the amount of wait time before they are deleted is less than or equal time to the number of yield;s in TFinalize.

   task TestTask{
   	let angleT = rand(0,360);
   	loop(15){
   	  loop(6){
   	    CreateShotA1(GetEnemyX(objBoss), GetEnemyY(objBoss), 4, angleT, 234, 5);
   	    CreateShotA1(GetEnemyX(objBoss), GetEnemyY(objBoss), 4, angleT+1, 234, 7);
   	    CreateShotA1(GetEnemyX(objBoss), GetEnemyY(objBoss), 4, angleT-1, 234, 7);
   	    angleT+=360/6;
   	  }
   	  PlaySE(bullet);
   	  loop(5){yield;}
   	  angleT+=19;
   	}
   }

Let us assume that this task is called when the boss has almost zero HP. It will continue to loop, waiting 5 frames each time. Since the objBoss is deleted after 1 frame in the sample TFinalize, the rest of the bullets will form at (0,0) and will not be deleted because the shot deletion functions were called BEFORE the bullets were created (since they have delay). That's where this comes in.

   	if(ObjEnemy_GetInfo(objBoss, INFO_LIFE) <= 0){return;}//Default kill to prevent (0,0) spawning

If you insert this right before the shots are created (after loop(15) in this case), and have a yield; in TFinalize, the task will be terminated on the spot if the boss does not exist. As a result, the window in which bullets can spawn will be dramatically shortened. If your wait time in TFinalize is greater than the maximum delay you have on a bullet, this code will prevent bullets from spawning into the next attack.

Basically, as CK Crash said, "You want TFinalize to be the absolute last thing to run." That way, all of your tasks have terminated and there is no chance of anything appearing after the script has been closed. An alternate solution is to yield until all bullet and graphic spawning tasks have ended before running TFinalize, although this will require other workarounds for death effects.

If all of this doesn't work, there is, of course, a cheap way out. Please don't run this unless you've deleted all existing shots using DeleteShotAll.

       let arr = GetShotIdInCircleA2(GetCenterX,GetCenterY,1000,TARGET_ENEMY);
       ascent(i in 0..length(arr)){Obj_Delete(arr[i]);}

This assumes that GetCenterX and GetCenterY have been defined (they can be found under Helpful Functions), and will get the ID of every single existing shot on the screen that has been fired by the enemy. It will then delete them all. Note that if you have 1000 bullets, indexing an array with 1000 values will take a while. That's why this should only be used as a last resort OR when you've already cleared out pretty much every existing enemy shot.

SetAutoDeleteObject(false);

If you SetAutoDeleteObject(false) for any reason or leave it as is (it is defaulted to false), you will have to delete all of your objects (graphics and effects, among other things) manually. Use DeleteShotAll to delete shots and lasers. You will have to manually delete Sprite and Primitive objects and other miscellaneous objects before the script terminates.

Note that many function libraries with graphics assume that SetAutoDeleteObject is set to true, and that not all of them have manual deletion of their objects (such as GTBot's original cut-in library). If SetAutoDeleteObject is being a pain, Notify User Events stored in the plural using NotifyEventAll in the single and have whatever objects you want to spawn at the end of a single spawn in the plural so that they will not be deleted in the single. SetAutoDeleteObject only auto deletes objects created in the file it is called in.

return; vs. break;

Now, the difference between return; and break;

break; breaks out of a loop such as a while, loop, ascent, or descent. It will kill the innermost loop. Use return (without a return value - simply a ;) to return a void value for the task, and the task will terminate on the spot. It can be helpful to use return; (but not as the last line of a task. There is a return; at the end of TFinalize because mkm had it there in the sample scripts. It serves no practical purpose and is merely decoration (as far as I know).

Parentheses or no parentheses?

From Drake:

"Danmakufu is a parsed language and one lazymode shortcut it provides is that you don't have to add the empty () to call a function with no parameters (whereas in compiled languages and whatnot you generally have to). I usually omit the parentheses myself, but when posting I would rather use them than not, since it makes it obvious that I'm talking about a function. That's all."

Source: [Link]

For functions and tasks, feel free to omit the parentheses. Unless it's loop(), in which case it is recommended that you either loop{ or loop(int){ rather than loop(){.

Threads and coroutines (Advanced)

From Blargel:

"I'd like to point out that the tasks that Danmakufu uses are not truly threads that run independently. If they were threads, they would indeed have some overhead processing that occurs when context switching. However, tasks are actually coroutines which are very different from threads. While threads are for concurrent processing, coroutines are still sequential so there is no context switching to process. It's really just a fancy way to give a sequence of directions to the program while writing it in a non-sequential way.

tl;dr Tasks are not threads and are not slow.

Also, have a [stackoverflow link]."

Source: [Link]

Note: Ultima has requested multithreading for ph3 and it is NOT POSSIBLE without mkm rewriting the engine. So unfortunately, it will not be happening anytime soon.

Regarding semicolons

From Drake:

"Everything is interpreted by Danmakufu's scripting engine, so it's basically just an interpreter quirk. Shot data scripts need no semicolons because no statements are being executed, it might as well be XML or something. Statements with # are headers and are also just interpreted. The #include statements are just replaced by the referenced script, so again nothing is being executed.

Not too sure about return not requiring semicolons since I always use them anyways. I do know Danmakufu allows you to omit semicolons if a statement is the only one in a bracket, like while(x > y){ z = a } but it's still really weird and I would never suggest omitting them just because you can."

Source: [Link]

#include in an #include

In Danmakufu, it is possible to #include inside of an included file. However, be aware that the order in which you include files and the nesting of includes determines whether or not an included file will work correctly. For example, if you define GetCenterX() in one include, other includes must include that file or be included after that file has been included in order to not bring up an error.

Commenting

//This is a comment
/*This is a comment*/
/*This
is 
a
comment
*/
///This may or may not be parsed as a comment. BEWARE.
/** This may or may not be parsed as a comment. BEWARE. **/
/* ****************
This is a comment.
**************** */

List of Helpful Functions

 function wait(let frames){
     loop(frames){yield;}
 }
 function WaitForZeroEnemy{
     while(length(GetAllEnemyID()) > 0){yield;}
 }
 function GetCenterX{
     return GetStgFrameWidth/2;
 }
 function GetCenterY{
     return GetStgFrameHeight/2;
 }
 function rand_int(min, max){
     return round(rand(min, max))
 }
 function sec(n) { return 1/cos(n); }
 function csc(n) { return 1/sin(n); }
 function cot(n) { return 1/tan(n); }
 function GetAtanAngle(obj,obj2){
     let angle=atan2(ObjMove_GetY(obj2)-ObjMove_GetY(obj),ObjMove_GetX(obj2)-ObjMove_GetX(obj));
     return angle;
 }
 function GetPointAngle(x1,y1,x2,y2){
     let angle=atan2(y1-y2,x1-x2);
     return angle;
 }
 function GetDistance(x1,y1,x2,y2){
     return(((x2-x1)^2+(y2-y1)^2)^(1/2));
 }
 function DelaySE(sfx, time){//Assumes that sfx is the path of a loaded sound effect.
   loop(time){
     yield;
   }
   PlaySE(sfx);
 }

NOTE: The following task DOES NOT WORK for Stage Enemies. After the frame variable hits 0, the enemies will take no damage for the rest of the bomb duration, and will most likely escape completely unscathed. Only use this for Boss Single scripts.

 task SetDamageRateAtFrame(obj, shot,bomb,frame){//Talos
   ObjEnemy_SetDamageRate(obj, 0,0);
   let time=frame;
   while(IsPlayerSpellActive){ //Prevents Bomb Damage from previous attack
     time--;
     yield;
   }
   while(time>0){
     time--;
     yield;
   }
   ObjEnemy_SetDamageRate(obj,shot,bomb);
 }

Debug Window

ph3 comes with a debug window that can be used for a large variety of purposes. In config, click the first box in order to use the Log Window.

The first tab is Log. It states everything that the program does, complete with timestamps. Info provides data such as fps, screen dimensions, number of objects, etc, as well as other helpful data. Task is a basic task manager that states the Hex of the task address as week as other information. Texture, Mesh, and Sound include all of the files loaded/in use by Danmakufu. This is a good place to check if you've actually loaded certain resources. In CommonData, the top screen shows AreaCommonData, while the bottom shows CommonData nested underneath AreaCommonData. All CommonData that are not specified to be AreaCommonData are under the first line, which is blank. Click that to show the values of CommonData. Finally, in Script, you can automatically terminate your script.

CREDITS

Although I wrote most of the guide, some others have helped clarify and write this guide. Thanks go to Ultima, Ozzy, CK Crash, and GTBot.