As was foretold, we've added advertisements to the forums! If you have questions, or if you encounter any bugs, please visit this thread: https://forums.penny-arcade.com/discussion/240191/forum-advertisement-faq-and-reports-thread/
Options

Danmakufu: Freeware SHMUP Programming

AroducAroduc regular
danmakufu1.jpg

Danmakufu is a pretty powerful freeware SHMUP language that's been used to build a couple games, such as Concealed Conclusion (or Concealed the Conclusion depending on your tolerance for Engrish).

A couple sample videos of the kinds of things it can do.
http://www.youtube.com/watch?v=kZt3fPp-Uv0
http://www.youtube.com/watch?v=1kbZSbhOkRg

And these are in no way exhaustive of its powers. The framerate is a little slow in those since my computer can't handle recording and playing at the same time.

The language used in Danmakufu is a C-base and can be written in pretty much any text editor you want. It's 100% English, but the documentation isn't stellar, in either language though I've been working on and off to write up a better manual for it. It supports doing all sorts of wacky things with the player, the surroundings, the way bullets work and are manipulated, etc etc etc. I've poked around quite a bit and am pretty comfortable with the engine (at least, the bullet/object control part, not so much the 3D background drawing part), though often short on inspiration.

dfsyy9.jpg

Where to get shit
Danmakufu itself
http://www.geocities.co.jp/SiliconValley-Oakland/9951/products/th_dnh.html (v.12m... yes, it's only 4 mb)

English Documentation
http://pooshlmer.com/touhouwiki/index.php/Danmakufu

Japanese Documentation
http://f27.aaa.livedoor.jp/~thdmhul/pukiwiki/pukiwiki.php

Alternative Age demo (early/primitive game made using it... good for sample scripts)
http://danmaq.com/products/thd/tha/thA_TR.zip (just unzip that into the scripts directory and then run Danmakufu).

A few samples of some more complex things
http://pooshlmer.com/touhouwiki/index.php/Touhou_Danmakufu:_Samples

Or if you're lazy, here's a zip with everything mentioned above (except the complex scripts) as well as Concealed Conclusion's replacement sprites for the ugly default white player character shadow along with the CC frame since I didn't notice that I had until well after it was uploaded.
http://www.megaupload.com/?d=BYO5U645

It'd be damn sexy if some of the code monkies at PA felt like strutting their stuff and trying to put out some either wacky fractal boss patterns or some theoretically unbeatable shit (nothing is unbeatable. Proof.) or just fucking about. Recording single patterns or stages only takes me about 2-3 minutes, so I'd be happy to post videos of people's work if they produced something especially neat. Hell, this kind of thing is something you could even put in your game resume/portfolio if you wanted to get into the business.

danmakufu3.jpg

I've also been putzing around with the various things you can do with it and even cobbled together a (very primitive) boss.

Some of my favorites of things I've done (caveat: I've never cleaned up the code or optimized it really, and especially on the full boss, I tried multiple things on different cards to get a glowing/flame trail effect, so it's a bit disjointed):

Sliding Puzzle
http://www.youtube.com/watch?v=XQin2jUUOmE
Code:
#東方弾幕風
#Title[Illusion Sign 「Sliding Puzzle」]
#Text[Chew on this.]
#Image[]
#BackGround[Default]
#Player[REIMU,MARISA]
#ScriptVersion[2]
#BGM[.\bgm\thC06.mp3]

script_enemy_main
{
   let count;
   let path = 5;
   let direct;
   let x=4;
   let y=9;
   let movex;
   let movey;
   let j;
   let i;

   @Initialize
   {
      count= -360;
      SetLife(2000);
      LastSpell;
      SetMovePosition02(GetCenterX, GetCenterY, 5);
      SetScore(38000);
      SetTimer(32);
      SetInvincibility(600);
      CutIn(YOUMU,"Illusion Sign 「Sliding Puzzle of DOOM」", 0, 0, 0, 0, 0);
      SetEnemyMarker(true);
   }

   @MainLoop
   {
	if (count == -300){
		CreateLaserA(1, 20, 0, 500, 10, RED01, 0);
		CreateLaserA(2, 420, 0, 500, 10, RED01, 0);
		CreateLaserA(3, 0, 0, 500, 10, RED01, 0);
		SetLaserDataA(1, 10, 90, 0, 0, 1, 0);
		SetLaserDataA(2, 10, 90, 0, 0, 1, 180);
		SetLaserDataA(3, 10, 0, 0, 0, 2, 90);
		SetLaserDataA(1, 160, NULL, 0, 0, 0, 0);
		SetLaserDataA(2, 200, NULL, 0, 0, 0, 0);
		SetLaserDataA(3, 240, NULL, 0, 0, 0, 0);
		SetShotKillTime(1,240);
		SetShotKillTime(2,240);
		SetShotKillTime(3,200);
		FireShot(1);
		FireShot(2);
		FireShot(3);
		}
	
	if (count == -110){
		i = 1;
		j = 1;
		while(i < 9){
			while (j < 10){
				if ((x==i) && (y == j)){}
				else {
					CreateShotA(i, i*50, j*50,60);
					SetShotDataA(i, 0, 0, 0, 0, 0, 0, RED03);
					SetShotKillTime(i, 90);
					if (!((i == 4) &&(j == 9))){FireShot(i);}
					}
				j++;
				}
			j = 1;
			i++;
			}
		}

        if (count > 0 && (count % 40 == 0) && (count < 1440)){
		direct = rand_int(0,3);
		movex = 0;
		movey = 0;
		
		if (direct == 0){movex = -1;}
		if (direct == 1){movex = 1;}
		if (direct == 2){movey = -1;}
		if (direct == 3){movey = 1;}
		
		if (x == 8){movex = (movex * movex);}
		if (x == 1){movex = -(movex * movex);} 
		if (y == 9){movey = (movey * movey);}
		if (y == 1){movey = -(movey * movey);}
		
		i = 1;
		j = 1;
		while(i < 9){
			while (j < 10){
				if (x == (i+movex) && y == (j+movey)){
					PhaseShot(i*50, j*50, 0, 0, GREEN03, 0, movex, movey);		
					}
		else if ((x==i) && (y == j)){}
				else {
					
					CreateShotA(i, i*50, j*50,0);
					SetShotDataA(i, 0, 0, 0, 0, 0, 0, RED03);
					SetShotKillTime(i, 40);
					FireShot(i);
				}
				j++;
			}
		j = 1;
		i++;
		}
	
	x = x-movex;
	y = y-movey;
	i = 1; 		
	}
	
	if (count == 1440){
		ascent(i in 0..1080){
			CreateShotA(i, x*50 + 40*cos(i), y*50 + 40*sin(i), 0);
			SetShotDataA(i, 0, 0, i, 0, 0, 0, RED21);
			SetShotDataA(i, 60, i%3 + 7, NULL, i%3, 0, 10, BLUE21);
			FireShot(i);
			}
		
		}
	count++;
      	yield;
   }

   @DrawLoop
   {
      SetTexture("script\img\ExRumia.png");
      DrawGraphic(GetX(),GetY());
      DrawText("Illusion Sign", GetClipMinX()+20, GetClipMinY()+16, 12, 210);
   }

   @Finalize
   {
      DeleteGraphic("script\img\ExRumia.png");
   }

   task PhaseShot(x, y, v, angle, graphic, delay, phaseX, phaseY)
   {
      let obj = Obj_Create(OBJ_SHOT);
      let count = 0;

      //Obj_SetPosition(obj,x,y);
      Obj_SetX(obj,x);
      Obj_SetY(obj,y);
      Obj_SetSpeed(obj,v);
      ObjShot_SetGraphic(obj, graphic);
      ObjShot_SetDelay(obj,delay);
      Obj_SetAngle(obj,angle);
    
    while (!Obj_BeDeleted(obj)){  
	
       if (count > 10 && count < 40){
	    Obj_SetSpeed(obj, 0);
            Obj_SetCollisionToPlayer(obj,false);
	    Obj_SetAlpha(obj, 100);
	    Obj_SetX(obj, Obj_GetX(obj) + (2*phaseX));
	    Obj_SetY(obj, Obj_GetY(obj) + (2*phaseY));
	    }
	if (count == 40){Obj_Delete(obj);}
	count++;
	yield;
	}     

   }
  }

}

Umbrella Clouds
http://www.youtube.com/watch?v=6kbxDCiQZ7I
Code:
#&#26481;&#26041;&#24382;&#24149;&#39080;
#Title[Storm Sign &#12300;Rain Cloud&#12301;]
#Text[Chew on this.]
#Image[]
#BackGround[Default]
#Player[REIMU,MARISA]
#ScriptVersion[2]
#BGM[.\bgm\thC15.mp3]

script_enemy_main
{
   let count;
   let array = [0,1,2,3,4,5,6];
   
   @Initialize
   {
      count=-60;
      SetLife(2000);
      SetGraphicRect(64,1,127,64);
      LoadGraphic("script\img\ExRumia.png");
      SetMovePosition02(GetCenterX, 120, 60);
      SetScore(38000);
      SetTimer(30);
      SetInvincibility(300);
      CutIn(YOUMU,"Storm Sign &#12300;Rain Cloud&#12301;", 0, 0, 0, 0, 0);
      SetEnemyMarker(true);
   }

   @MainLoop
   {
      	SetCollisionA(GetX, GetY, 32);
      	SetCollisionB(GetX, GetY, 24);
	if (count == 0){
		ascent(i in 0..7) {
			if (i % 2 == 1 ){
				let obj = Obj_Create(OBJ_SHOT);
      
				//Obj_SetPosition(obj,x,y);
				Obj_SetX(obj,5);
      				Obj_SetY(obj,rand(100,200));
			        Obj_SetSpeed(obj,rand(1,2));
				ObjShot_SetGraphic(obj, WHITE03);
				ObjShot_SetDelay(obj,i*20);
		        	Obj_SetAngle(obj,rand(350,370));
			        Obj_SetCollisionToObject(obj, true);
				array[i] = obj;
				}
			if (i % 2 == 0){
				let obj = Obj_Create(OBJ_SHOT);
      
				//Obj_SetPosition(obj,x,y);
				Obj_SetX(obj,480);
      				Obj_SetY(obj,rand(100,200));
			        Obj_SetSpeed(obj,rand(1,2));
				ObjShot_SetGraphic(obj, WHITE03);
				ObjShot_SetDelay(obj,i*20);
		        	Obj_SetAngle(obj,rand(170,190));
			        Obj_SetCollisionToObject(obj, true);
				array[i] = obj;
				}		
			}
		}

	if (count > 300){ascent(i in 0..4){Raindrop(rand(40,420), 0, rand(4,6), rand(85,95), BLUE11, 60, array);}}

	ascent(i in 0..7){
		let obj = array[i];
		if (Obj_GetX(obj) > 480){
			Obj_SetAngle(obj, rand(350,370));
			Obj_SetX(obj, 5);
			Obj_SetY(obj, rand(100,200));
			}
		if (Obj_GetX(obj) < 5){
			Obj_SetAngle(obj, rand(170,190));
			Obj_SetX(obj, 450);
			Obj_SetY(obj, rand(100,200));
			}
		}
	count++;
      	yield;
   }

   @DrawLoop
   {
      SetTexture("script\img\ExRumia.png");
      DrawGraphic(GetX(),GetY());
      DrawText("Illusion Sign", GetClipMinX()+20, GetClipMinY()+16, 12, 210);
   }

   @Finalize
   {
      DeleteGraphic("script\img\ExRumia.png");
   }

   task Raindrop(x, y, v, angle, graphic, delay, clouds)
   {
      let obj = Obj_Create(OBJ_SHOT);

      //Obj_SetPosition(obj,x,y);
      Obj_SetX(obj,x);
      Obj_SetY(obj,y);
      Obj_SetSpeed(obj,v);
      ObjShot_SetGraphic(obj, graphic);
      ObjShot_SetDelay(obj,delay);
      Obj_SetAngle(obj,angle);
      Obj_SetCollisionToObject(obj, true);

      
    while (!Obj_BeDeleted(obj)){
	ascent(i in 0..7){
		let cloud = clouds[i];
		if (Collision_Obj_Obj(obj, cloud)){Obj_Delete(obj);}
		}
	yield;
	}   

  }

}

Homing/Seeking Shots
http://www.youtube.com/watch?v=tnJwD8b9UJg
Code:
#&#26481;&#26041;&#24382;&#24149;&#39080;
#Title[Hive Sign &#12300;Wrath of the Swarm&#12301;]
#Text[Chew on this.]
#Image[]
#BackGround[Default]
#Player[REIMU,MARISA]
#ScriptVersion[2]
#BGM[.\bgm\thC13.mp3]

script_enemy_main
{
   let count;
   let speed;
   let flip = 0;

   @Initialize
   {
      count=-60;
      SetLife(2000);
      SetGraphicRect(64,1,127,64);
      LoadGraphic("script\img\ExRumia.png");
      SetMovePosition02(GetCenterX, 120, 60);
      SetScore(38000);
      SetMovePosition02(GetCenterX, 120, 60);
      SetTimer(50);
      SetInvincibility(120);
      CutIn(YOUMU,"Hive Sign &#12300;Wrath of the Swarm&#12301;", 0, 0, 0, 0, 0);
      SetEnemyMarker(true);
   }

   @MainLoop
   {
      if (count % 90 == 0)
      {
         speed = rand(2,4);
	 if (flip % 2 == 0){
         	HomingShot(GetX, GetY, speed, GetAngleToPlayer - 20, YELLOW21, 0);
	 	HomingShot(GetX, GetY, speed, GetAngleToPlayer + 20, YELLOW21, 0);
		HomingShot(GetX, GetY, speed, GetAngleToPlayer, BLUE21, 0);
	 }
	 else {
		HomingShot(GetX, GetY, speed, GetAngleToPlayer, YELLOW21, 0);
         	HomingShot(GetX, GetY, speed, GetAngleToPlayer + 20, BLUE21, 0);
	 	HomingShot(GetX, GetY, speed, GetAngleToPlayer - 20, BLUE21, 0);
		}

        
	 flip++;
      }
      
      if (count == 180){
        count = 0;
	ascent(i in 0..60){
          SeekingShot(GetX, GetY, 6, rand(200,340), RED32, 0, i);
        }
      }

      SetCollisionA(GetX(), GetY(), 32);
      SetCollisionB(GetX(), GetY(), 24);
      count++;
      yield;
   }

   @DrawLoop
   {
      SetTexture("script\img\ExRumia.png");
      DrawGraphic(GetX(),GetY());
      DrawText("Celia Versio", GetClipMinX()+20, GetClipMinY()+16, 12, 210);
   }

   @Finalize
   {
      DeleteGraphic("script\img\ExRumia.png");
   }

   task HomingShot(x, y, v, angle, graphic, delay)
   {
      let obj = Obj_Create(OBJ_SHOT);
      let count = 0;

      //Obj_SetPosition(obj,x,y);
      Obj_SetX(obj,x);
      Obj_SetY(obj,y);
      Obj_SetSpeed(obj,v);
      ObjShot_SetGraphic(obj, graphic);
      ObjShot_SetDelay(obj,delay);
      Obj_SetAngle(obj,angle);

      while (!Obj_BeDeleted(obj))
      {  
	    if (count < 900 && count > 30 ){
	            
                    let angle = Obj_GetAngle(obj);
                    let toAngle = atan2(GetPlayerY - Obj_GetY(obj), GetPlayerX - Obj_GetX(obj));
                    let targetAngle = toAngle - angle;

		    if (targetAngle < 0){targetAngle += 360;}
		    if (targetAngle > 360){targetAngle -= 360;}
		    
        	    if(targetAngle >= 0 && targetAngle <= 180){angle+=3;}
            	    else {angle-=3;}

		    if (angle < 0){angle += 360;}
		    if (angle > 360){angle -= 360;}

            	    Obj_SetAngle(obj, angle);
	}
            count++;
         
         yield;
      }
   }

  task SeekingShot(x, y, v, angle, graphic, delay, wait)
   {
      let obj = Obj_Create(OBJ_SHOT);
      let count = 0;

      //Obj_SetPosition(obj,x,y);
      Obj_SetX(obj,x);
      Obj_SetY(obj,y);
      Obj_SetSpeed(obj,v);
      ObjShot_SetGraphic(obj, graphic);
      ObjShot_SetDelay(obj,delay);
      Obj_SetAngle(obj,angle);

      while (!Obj_BeDeleted(obj))
      {  
	    if (count == 15){
	 	Obj_SetSpeed(obj, 0);
		}
	    if (count > (75+wait) && count < (90+wait)){Obj_SetY(obj,(Obj_GetY(obj)-2));
		Obj_SetAngle(obj,(Obj_GetAngle(obj)+24));}
	    if (count == (90+wait)){	           
                    let toAngle = atan2(GetPlayerY - Obj_GetY(obj), GetPlayerX - Obj_GetX(obj));
            	    Obj_SetAngle(obj, toAngle);
		    Obj_SetSpeed(obj,12);
	}
            count++;
         
         yield;
      }
   }

}

'Black Hole' bombs
http://www.youtube.com/watch?v=2QZvjMtfumo
Code:
#TouhouDanmakufu
#Title[Hive Sign "Test"]
#Text[Test script]
#Player[FREE]
#ScriptVersion[2]
#BGM[.\bgm\thC31.mp3]

script_enemy_main {
    let ImgBoss = "script\img\ExRumia.png";

    let count = 0;

    @Initialize {

        SetLife(2000);
        SetTimer(50);
        SetScore(1000000);

        SetMovePosition02(GetCenterX, GetClipMinY + 120, 120);
	CutIn(YOUMU,"Spatial Sign &#12300;Gravity Bomb&#12301;", 0, 0, 0, 0, 0);

        LoadGraphic(ImgBoss);
        SetTexture(ImgBoss);
        SetGraphicRect(0, 0, 64, 64);
    }

    @MainLoop {
        SetCollisionA(GetX, GetY, 32);
        SetCollisionB(GetX, GetY, 16);

	if (count > 0 && count % 180 == 0){BlackHole(GetX, GetY, GetAngleToPlayer, WHITE03, 30);}

        count++;
	yield;
    }

    @DrawLoop {
        DrawGraphic(GetX, GetY);
    }

    @Finalize {
        DeleteGraphic(ImgBoss);
    }


task BlackHole(x, y, angle, graphic, delay)
   {
      let obj = Obj_Create(OBJ_SHOT);
      let count2 = 0;
      let suck = 0;	
      let playerY;
      let playerX;
      let alpha = 255;

      Obj_SetX(obj,x);
      Obj_SetY(obj,y);
      Obj_SetSpeed(obj,4);
      ObjShot_SetGraphic(obj, graphic);
      ObjShot_SetDelay(obj,delay);
      Obj_SetAngle(obj,angle);

      while (!Obj_BeDeleted(obj))
      {  
	if (count2 == 0){
		playerX = GetPlayerX;
		playerY = GetPlayerY;
		}
	if (count2 % 3 == 0 && suck == 1){
		if(GetPlayerX > Obj_GetX(obj)){SetPlayerX(GetPlayerX-2);}
		if(GetPlayerX < Obj_GetX(obj)){SetPlayerX(GetPlayerX+2);}
		if(GetPlayerY < Obj_GetY(obj)){SetPlayerY(GetPlayerY+2);}
		if(GetPlayerY > Obj_GetY(obj)){SetPlayerY(GetPlayerY-2);}
		}
	
	if (suck == 1 && count2 < 200){
		
			let angle = rand(0,360);
			CreateShotA(1,75*cos(angle)+Obj_GetX(obj), 75*sin(angle)+Obj_GetY(obj), 5);
			SetShotDataA(1, 0, 0, angle+180, 0, 1, 6, WHITE11);
			SetShotDataA(1, 5, 0, angle+180, 0, 1, 6, WHITE11);
			SetShotDataA(1, 18, 0, NULL, 0, 0, 0, WHITE11);
			SetShotDataA(1, 219-count2, rand(1,4), NULL, 0, 0, 0, WHITE11); 
			FireShot(1);
			
		}

	    if ((absolute(Obj_GetX(obj) - playerX) < 25) && (absolute(Obj_GetY(obj) - playerY) < 25)){	           
                Obj_SetSpeed(obj,0);    
		suck = 1;
		alpha = alpha - 3;
		Obj_SetAlpha(obj, alpha);
		}

	if (count2 == 240){Obj_Delete(obj);}


        count2++;
         
        yield;
      }
   }

}

Volcano/Flame themed boss
http://www.youtube.com/watch?v=Kf9Spq-KSME
Code: (well, just showing the snippet defining the subscripts to use as the patterns)
#TouhouDanmakufu[Plural]
#Title[Volcano Boss]
#Text[Test]
#Image[.\img\Meiling.png]
#BackGround[Default]
#BGM[.\bgm\thC32.mp3]
#Player[FREE]
#ScriptVersion[2]

#ScriptPathData
    #ScriptPath[.\Volcano0_0.txt]
    #ScriptPath[.\Volcano0_1.txt]
#ScriptNextStep
    #ScriptPath[.\Volcano1_0.txt]
    #ScriptPath[.\Volcano1_1.txt]
    #ScriptPath[.\Volcano1_2.txt]
#ScriptNextStep
    #ScriptPath[.\Volcano2_0.txt]
    #ScriptPath[.\Volcano2_1.txt]
    #ScriptPath[.\Volcano2_2.txt]
#ScriptNextStep
    #ScriptPath[.\Volcano9_9.txt]
#EndScriptPathData

Aroduc on

Posts

  • Options
    JaninJanin Registered User regular
    edited November 2007
    What do you think the chances are of getting the source code to Danmakufu itself? It'd be interested in adding support for Unicode, Vorbis, etc.

    Janin on
    [SIGPIC][/SIGPIC]
  • Options
    AroducAroduc regular
    edited November 2007
    I have no clue. I do know that somebody over at ShrineMaiden is working on some alternate. I also found a large dump of scripts from various Japanese people and it's really strange to see the totally different structure that they use.

    Example:
    This script shoots two shots (one delayed) at the same random location (in the centerish area) and when they 'collide' (there's no actual collision detection, it's just timed) one explodes and the other changes course upwards. It's really easy to do with just a pair of objects, and it'd be so so much more readable (at least to me), but they've got some really screwy way of going about it that I'm still wrapping my head around. It's definitely very very different than I (and from what I've seen of the other English danmaku guys) would write it. I guess the main advantage is that they're controlling things through the objects instead of through the boss (the Main{} process), which lets them shorthand a lot of stuff (ie using GetX() instead of Obj_GetX(obj)) but *shrug* Hell, you could just do similar things by defining an enemy object's graphic as a bullet, which gives you access to the somewhat more useful "move here in X frames" functions for things like that sort of collision effect.
    #&#26481;&#26041;&#24382;&#24149;&#39080;
    #Title[&#36890;&#24120;&#25915;&#25731;&#65288;&#39764;&#21091;&#65293;&#65300;&#65289;]
    #Text[Level:Normal]
    #Image[.\img\END\END_n14.png]
    #BackGround[Default]
    #Player[FREE]
    #ScriptVersion[2]
    
    script_enemy_main{
    
    	// &#30011;&#20687;&#21450;&#12403;&#21177;&#26524;&#38899;&#12501;&#12449;&#12452;&#12523;&#12398;&#22580;&#25152;
    	let csd=GetCurrentScriptDirectory();
    	let imgBoss=csd~"img\END\ExRumia.png";
    	let imgEnemy=csd~"img\END\Seal00.png";
    	let wav1=csd~"wav\END\swing00.wav";
    	let wav2=csd~"wav\END\arrow01.wav";
    	let wav3=csd~"wav\END\byoro07.wav";
    	let wav4=csd~"wav\END\chari08.wav";
    
    	// &#24231;&#27161;&#35373;&#23450;
    	let cenX=GetCenterX();
    	let cenY=GetCenterY();
    	let minX=GetClipMinX();
    	let maxX=GetClipMaxX();
    	let minY=GetClipMinY();
    	let maxY=GetClipMaxY();
    
    	let enemyX=[minX+40,maxX-40];
    	let enemyY=[minY+40,minY+40];
    
    	// -------------------------------------------------------------------------
    	@Initialize{
    
    		// &#30011;&#20687;&#21450;&#12403;&#21177;&#26524;&#38899;&#12434;&#12525;&#12540;&#12489;
    		LoadGraphic(imgBoss);
    		LoadGraphic(imgEnemy);
    		LoadSE(wav1);
    		LoadSE(wav2);
    		LoadSE(wav3);
    		LoadSE(wav4);
    
    		// &#12473;&#12506;&#12523;&#12459;&#12540;&#12489;&#35373;&#23450;&#65288;&#12459;&#12540;&#12489;&#21517;&#12289;&#12473;&#12467;&#12450;&#12289;&#21046;&#38480;&#26178;&#38291;&#31561;&#65289;
    		SetTimer(63);
    		SetEnemyMarker(true);
    
    		// &#12508;&#12473;&#12473;&#12486;&#12540;&#12479;&#12473;&#35373;&#23450;&#65288;&#12521;&#12452;&#12501;&#12289;&#12480;&#12513;&#12540;&#12472;&#29575;Shot&#37;,Bomb%&#31561;&#65289;
    		SetLife(2000);
    		SetDamageRate(60,20);
    
    		Main;
    
    	}
    
    	// -------------------------------------------------------------------------
    	@MainLoop{
    
    		// &#12508;&#12473;&#24403;&#12383;&#12426;&#21028;&#23450;&#65288;A:&#33258;&#24382;,B:&#20307;&#24403;&#12383;&#12426;&#65289;
    		SetCollisionA(GetX(),GetY(),32);
    		SetCollisionB(GetX(),GetY(),24);
    
    		yield;
    
    	}
    
    	// &#12513;&#12452;&#12531;&#20966;&#29702; --------------------------------------------------------------
    	task Main{
    
    		yield;
    
    		// &#21021;&#26399;&#31227;&#21205;
    		SetInvincibility(180);
    		SetMovePosition02(cenX,minY+100+rand(-1,1),120);
    		wait(121);
    
    		// &#20351;&#12356;&#39764;&#21484;&#21914;
    		ascent(let i in 0..2){CreateEnemyFromScript("Familiar",enemyX[i],enemyY[i],0,0,i);}
    		PlaySE(wav1);
    		wait(60);
    
    		let assign=0;
    
    		loop{
    
    			// &#28856;&#35010;&#24382;
    			SpreadShot(assign);
    			assign=1-assign;
    			wait(30);
    
    			// &#31227;&#21205;
    			move(60);
    			wait(90);
    
    		}
    
    	}
    
    	// &#28856;&#35010;&#24382; ------------------------------------------------------------------
    	task SpreadShot(let assign){
    
    		// &#21021;&#26399;&#35373;&#23450; ----------------------------------------
    		let obj=Obj_Create(OBJ_SHOT);
    		let spX=cenX+rand(-100,100);
    		let spY=cenY+rand(-20,20);
    		let spLength=((spX-GetX())^2+(spY-GetY())^2)^0.5;
    		let speed=spLength/50;
    		let angle=atan2(spY-GetY(),spX-GetX());
    
    		Obj_SetX(obj,GetX());
    		Obj_SetY(obj,GetY());
    		Obj_SetSpeed(obj,speed);
    		Obj_SetAngle(obj,angle);
    		ObjShot_SetDelay(obj,10);
    		ObjShot_SetGraphic(obj,WHITE32);
    		ObjShot_SetBombResist(obj,true);
    
    		// &#12513;&#12452;&#12531;&#20966;&#29702; --------------------------------------
    		PlaySE(wav2);
    		wait(40);
    
    		// &#23556;&#25731;&#12524;&#12540;&#12470;&#12540;
    		spLength=((spX-enemyX[assign])^2+(spY-enemyY[assign])^2)^0.5;
    		speed=spLength/20;
    		angle=atan2(spY-enemyY[assign],spX-enemyX[assign]);
    		let width=10;
    		let blur=30;
    		let graphic=AQUA01;
    
    		SetShotDirectionType(ABSOLUTE);
    		CreateLaserC(0,enemyX[assign],enemyY[assign],width,blur,graphic,0);
    		SetLaserDataC(0,0,speed,angle,0,0,0);
    		SetLaserDataC(0,20,NULL,-angle,0,0,0);
    		FireShot(0);
    		PlaySE(wav3);
    		wait(20);
    
    		// &#28856;&#35010;
    		let way=90;
    		let baseAngle=0;
    		let wayAngle=360/way;
    		graphic=AQUA32;
    
    		SetShotDirectionType(ABSOLUTE);
    		ascent(let i in 0..way){
    			speed=rand(1,4);
    			angle=baseAngle+wayAngle*i;
    			CreateShot01(spX,spY,speed,angle,graphic,0);
    		}
    		PlaySE(wav4);
    
    		// &#28040;&#28357;
    		Obj_Delete(obj);
    
    	}
    
    	// &#31227;&#21205; --------------------------------------------------------------------
    	task move(let moveFr){
    
    		let x=GetX();
    		let y=GetY();
    		let mx=rand(40,80);
    		let my=rand(20,40);
    		if(x>=GetPlayerX()){
    			if((x-mx)>=(minX+100)){mx=-mx;}
    		}
    		else{
    			if((x+mx)>=(maxX-100)){mx=-mx;}
    		}
    		if(y>=(minY+100)){my=-my;}
    
    		SetMovePosition02(x+mx,y+my,moveFr);
    
    	}
    
    	// &#24453;&#12385; --------------------------------------------------------------------
    	function wait(let fr){
    
    		loop(fr){yield;}
    
    	}
    
    	// -------------------------------------------------------------------------
    	@Finalize{
    
    		// &#12525;&#12540;&#12489;&#12375;&#12383;&#12501;&#12449;&#12452;&#12523;&#12434;&#21066;&#38500;
    		DeleteGraphic(imgBoss);
    		DeleteGraphic(imgEnemy);
    		DeleteSE(wav1);
    		DeleteSE(wav2);
    		DeleteSE(wav3);
    		DeleteSE(wav4);
    
    	}
    
    	// -------------------------------------------------------------------------
    	@DrawLoop{
    
    		// &#12508;&#12473;&#25551;&#30011;
    		SetTexture(imgBoss);
    		if(GetSpeed()==0){SetGraphicRect(1,1,64,64);}
    		else if(GetAngle()>-90&&GetAngle()<=90){SetGraphicRect(192,1,255,64);}
    		else {SetGraphicRect(128,1,191,64);}
    		SetAlpha(255);
    		SetColor(255,255,255);
    		DrawGraphic(GetX(),GetY());
    
    	}
    
    }
    
    // &#20351;&#12356;&#39764; ----------------------------------------------------------------------
    script_enemy Familiar{
    
    	// &#30011;&#20687;&#21450;&#12403;&#21177;&#26524;&#38899;&#12501;&#12449;&#12452;&#12523;&#12398;&#22580;&#25152;
    	let csd=GetCurrentScriptDirectory();
    	let imgEnemy=csd~"img\END\Seal00.png";
    
    	// &#24231;&#27161;&#35373;&#23450;
    	let cenX=GetCenterX();
    	let cenY=GetCenterY();
    	let minX=GetClipMinX();
    	let maxX=GetClipMaxX();
    	let minY=GetClipMinY();
    	let maxY=GetClipMaxY();
    
    	let count=0;
    	let enemyNum=GetArgument();
    	let x0=GetX();
    	let y0=GetY();
    	SetX(GetEnemyX());
    	SetY(GetEnemyY());
    
    	// -------------------------------------------------------------------------
    	@Initialize{
    
    		// &#12473;&#12486;&#12540;&#12479;&#12473;&#35373;&#23450;&#65288;&#12521;&#12452;&#12501;&#31561;&#65289;
    		SetLife(1);
    
    		Main;
    
    	}
    
    	// -------------------------------------------------------------------------
    	@MainLoop{
    
    		count++;
    		yield;
    
    	}
    
    	// &#12513;&#12452;&#12531;&#20966;&#29702; --------------------------------------------------------------
    	task Main{
    
    		yield;
    
    		// &#31227;&#21205;
    		let angle0=atan2(y0-GetY(),x0-GetX());
    		SetMovePositionHermite(x0,y0,200,angle0,0,0,60);
    
    	}
    
    	// &#24453;&#12385; --------------------------------------------------------------------
    	function wait(let fr){
    
    		loop(fr){yield;}
    
    	}
    
    	// -------------------------------------------------------------------------
    	@DrawLoop{
    
    		// &#26412;&#20307;&#25551;&#30011;
    		SetTexture(imgEnemy);
    		SetGraphicRect(0,0,64,64);
    		SetAlpha(255);
    		SetColor(255,255,255);
    		SetGraphicScale(0.75,0.75);
    		SetGraphicAngle(0,0,count*2);
    		DrawGraphic(GetX(),GetY());
    
    	}
    
    	// -------------------------------------------------------------------------
    	@Finalize{
    
    	}
    
    }
    

    Alternate post:
    I've created lightning!
    http://seiha.org/images/zappy.avi
    I haven't cleaned the code yet, bolts were originally meant to arc through the clouds a lot more visibly, then I cut that, but some fragments remain. They're easier to see on the real thing anyway. It also needs something more. Maybe some rain or small explosions when the lightning exits the bottom of the stage. *shrug* The cloud effect is also fun. Basically a ton of shots with random low range alpha blending that fade in and out.
    #Title[Storm Sign &#12300;Zeus&#12301;]
    #Text[Zzzap.]
    #Image[]
    #BackGround[Default]
    #Player[REIMU,MARISA]
    #ScriptVersion[2]
    #BGM[.\bgm\thC13.mp3]
    
    script_enemy_main
    {
    	let count;
    	
    
       @Initialize
       {
          count=-60;
          SetLife(2000);
          SetGraphicRect(64,1,127,64);
          LoadGraphic("script\img\ExRumia.png");
          SetMovePosition02(GetCenterX, 120, 60);
          SetScore(38000);
          SetMovePosition02(GetCenterX, 120, 60);
          SetTimer(50);
          SetInvincibility(120);
          CutIn(YOUMU,"Storm Sign &#12300;Zeus&#12301;", 0, 0, 0, 0, 0);
          SetEnemyMarker(true);
       }
    
       @MainLoop
       {
    	loop(10){PuffyCloud(rand(GetClipMinX, GetClipMaxX), rand(GetClipMinY+100,GetClipMinY), rand(0,360));}
     
    	if (count % 10 == 0){HorizLightning(rand(GetClipMinX, GetClipMaxX), rand(GetClipMinY+80,GetClipMinY), rand(0,360));}
    	
    
          SetCollisionA(GetX(), GetY(), 32);
          SetCollisionB(GetX(), GetY(), 24);
          count++;
          yield;
       }
    
       @DrawLoop
       {
          SetTexture("script\img\ExRumia.png");
          DrawGraphic(GetX(),GetY());
          DrawText("Celia Versio", GetClipMinX()+20, GetClipMinY()+16, 12, 210);
       }
    
       @Finalize
       {
          DeleteGraphic("script\img\ExRumia.png");
       }
    
      task PuffyCloud(x, y, angle)
       {
    	let obj = Obj_Create(OBJ_SHOT);
    		
    	Obj_SetX(obj,x);
    	Obj_SetY(obj,y);
    	Obj_SetSpeed(obj,rand(0.25,0.75));
    	ObjShot_SetGraphic(obj, WHITE04);
    	ObjShot_SetDelay(obj,0);
    	Obj_SetAngle(obj, angle);     
    	Obj_SetAlpha(obj, rand(25,50));        
    	Obj_SetCollisionToPlayer(obj, false);
    
    	while (!Obj_BeDeleted(obj)){
    		if (count > 120){ObjShot_FadeDelete(obj);}		
    		count++;
             	yield;
        		}
       	}
    
    task HorizLightning(x, y, angle)
       {
            let obj = Obj_Create(OBJ_LASER);
    	let count = 0;
    
    	Obj_SetPosition(obj, x, y);
    	ObjShot_SetGraphic(obj, BLUE05);
    	ObjLaser_SetWidth(obj, 5);
    	ObjLaser_SetLength(obj, rand(40,50));
    	Obj_SetAngle(obj,angle);
    	ObjLaser_SetSource(obj,false);
    	ObjShot_SetDelay(obj, 5); 
    
          
    	while (!Obj_BeDeleted(obj)){
    		
    		if (ObjLaser_GetEndX(obj) < GetClipMinX + 32){angle = angle-180;}
    		if (ObjLaser_GetEndX(obj) > GetClipMaxX - 32){angle = angle-180;}
    		
    		if (ObjLaser_GetEndY(obj) > GetClipMinY + 100){
    			if (count < 10){
    				CreateShotA(1, ObjLaser_GetEndX(obj), ObjLaser_GetEndY(obj), 60);
    				SetShotKillTime(1, 0);
    				FireShot(1);count = 15;
    				}
    		
    			if (count == 45){
    				VertLightning(ObjLaser_GetEndX(obj) - 10*cos(angle), ObjLaser_GetEndY(obj)-20, rand(60,120));
    				Obj_Delete(obj);
    				}
    			}
    		else if (count == 5){
    			HorizLightning(ObjLaser_GetEndX(obj), ObjLaser_GetEndY(obj), rand(angle-15,angle+15));
    			}
    		else if (count == 10){Obj_Delete(obj);}
    		
    				
    		
    
    		count++;
    		yield;
    		}   
      }
    
     task VertLightning(x, y, angle)
       {
          let obj = Obj_Create(OBJ_LASER);
          let count = 0;
          let newAngle = rand(60,120);
    
    	Obj_SetPosition(obj, x, y);
    	ObjShot_SetGraphic(obj, BLUE05);
    	ObjLaser_SetWidth(obj, 20);
    	ObjLaser_SetLength(obj, rand(40,50));
    	Obj_SetAngle(obj,angle);
    	ObjLaser_SetSource(obj,false);
    	ObjShot_SetDelay(obj, 35); 
    
          
    	while (!Obj_BeDeleted(obj)){
    		
    		if (count == 1){
    			if (rand_int(0,10) == 5){
    				VertLightning(ObjLaser_GetEndX(obj) - 10*cos(angle), ObjLaser_GetEndY(obj)-20, rand(30,150));
    				}
    			VertLightning(ObjLaser_GetEndX(obj) - 10*cos(angle), ObjLaser_GetEndY(obj)-20, rand(60,120));
    
    			}
    		
    		if (count == 45){Obj_Delete(obj);}		
    
    		count++;
    		yield;
    		}   
      }
    }
    

    Aroduc on
  • Options
    JaninJanin Registered User regular
    edited November 2007
    Well, I feel I owe the Touhou community in general something for all the great music they produce (I've never actually played the games). However, the code you posted makes me want to stab out my eyes. May be the first thing I do will be to write a language that compiles down to that stuff.

    Janin on
    [SIGPIC][/SIGPIC]
  • Options
    AroducAroduc regular
    edited November 2007
    Which? The Japanese or mine? :P

    Aroduc on
  • Options
    JaninJanin Registered User regular
    edited November 2007
    Aroduc wrote: »
    Which? The Japanese or mine? :P

    Honestly, both. It's not the contents of the code I object to, it's the look and feel of the language itself. I get the feeling the guy who programming Danmaku-fu didn't have any experience in designing languages, and hadn't encountered many beyond C. In a week or two I'll be stuck on an airplane 16 hours both ways, and I've always wanted to learn how to create a compiler, so maybe I'll use this as an excuse.

    Janin on
    [SIGPIC][/SIGPIC]
  • Options
    JaninJanin Registered User regular
    edited November 2007
    A couple questions about this. I'll use your Storm Sign code to ask, since I assume that is what you'd have most recently in your mind.

    In the PuffyCloud task, you do not define a "count" variable like you do in the other tasks. Does this mean that the global variable in script_enemy_main is used?

    Also in PuffyCloud, you use Obj_SetX and Obj_SetY rather than the Obj_SetPosition in the other tasks. Why?

    For PuffyCloud (again), why do you use this while construct:
    while (!Obj_BeDeleted(obj)){
    	if (count > 120){ObjShot_FadeDelete(obj);}               
    	count++;
    	yield;
    }
    

    rather than simply:
    while (count <= 120) { count++; yield; }
    ObjShot_FadeDelete (obj);
    

    In the HorizLightning and VertLightning tasks, you define count = 0 at the start. Does this define a new variable distinct from that defined at the start of script_enemy_main, or does it set the value of the global variable to be 0?

    Why do you sometimes call GetClipMinX or similar with parenthesis, and sometimes not?

    Janin on
    [SIGPIC][/SIGPIC]
  • Options
    AroducAroduc regular
    edited November 2007
    Most of the answers are: "Because I'm lazy and use a generic template for objects." Like I said, I rarely go through and clean stuff up. Setting the delay is also totally unnecessary and could be removed. PuffyCloud shouldn't even take in any variables really since it's always the same in the 'final' version for that. Though not making a new 'count' variable was just an oversight. It automatically passes the variables unless you declare a new one. I must have accidently deleted 'count' from my template without realizing. It basically just makes the 'cloud' much much denser since it's no longer FadeDeleting the objects as soon as they spawn.

    I was originally toying with having the cloud particles control the lightning (or spawn off little puffs of themselves), so they were meant to be a lot more complex *shrug*

    The parens for GetClipMin/MaxX/Y is a leftover from the generic declaration template that I poached. It's totally unneeded and I don't think I've ever used them. They do absolutely nothing. Same for the GetX/GetY variables in the boss definition functions.

    Aroduc on
  • Options
    JaninJanin Registered User regular
    edited November 2007
    One last question before I can begin testing my own - is there any way to replace the sprites with something...blander? I only have one computer running Windows, and having little girls launching fireballs at each other would raise eyebrows. Alternatively, is there any way to run Danmaku-fu on a PowerPC Mac?

    Janin on
    [SIGPIC][/SIGPIC]
  • Options
    AroducAroduc regular
    edited November 2007
    Janin wrote: »
    One last question before I can begin testing my own - is there any way to replace the sprites with something...blander? I only have one computer running Windows, and having little girls launching fireballs at each other would raise eyebrows. Alternatively, is there any way to run Danmaku-fu on a PowerPC Mac?

    I have no idea about anything to do with Macs.

    The sprite replacement is relatively easy. This is the portion that defines the graphic for the boss:
    SetGraphicRect(64,1,127,64);
          LoadGraphic("script\img\ExRumia.png");
    

    Using this image
    exrumiagt1.png

    The player sprite images can be similarly defined, but I've never really putzed around with it. You could just replace the sprite images (ie, these)
    stgplayer01jx3.png with whatever you wanted though.

    Aroduc on
  • Options
    JaninJanin Registered User regular
    edited November 2007
    I've been putzing around with your rain/umbrella code to learn my way around the system, putting in comments on what some of the blocks do so I can figure out the rest later. I discovered that if you die, the umbrellas won't respawn and you'll get hammered by a wave of rain, so I'm going to try to fix that as a learning exercise.

    In case Aroduc and I are not the only people in the world reading this thread, here is the commented code:
    #TouhouDanmakufu
    #Title[Storm Sign &#12300;Rain Cloud&#12301;]
    #Text[Chew on this.]
    #Image[]
    #BackGround[Default]
    #Player[REIMU,MARISA]
    #ScriptVersion[2]
    #BGM[.\bgm\thC15.mp3]
    
    script_enemy_main
    {
    	let count;
    	let array = [0,1,2,3,4,5,6];
     	
    	@Initialize
    	{
    		count=-60;
    		SetLife(2000);
    		SetGraphicRect(64,1,127,64);
    		LoadGraphic("script\img\ExRumia.png");
    		SetMovePosition02(GetCenterX, 120, 60);
    		SetScore(38000);
    		SetTimer(30);
    		SetInvincibility(300);
    		CutIn(YOUMU,"Storm Sign &#12300;Rain Cloud&#12301;", 0, 0, 0, 0, 0);
    		SetEnemyMarker(true);
    	}
    	
    	@MainLoop
    	{
    		// No idea
    		SetCollisionA(GetX, GetY, 32);
    		SetCollisionB(GetX, GetY, 24);
    		
    		// Create the umbrellas after 60 frames
    		if (count == 0)
    		{
    			ascent(i in 0..7)
    			{
    				let obj = Obj_Create(OBJ_SHOT);
    				
    				Obj_SetY(obj,rand(100,200));
    				Obj_SetSpeed(obj,rand(1,2));
    				ObjShot_SetGraphic(obj, WHITE03);
    				ObjShot_SetDelay(obj,i*20);
    				Obj_SetCollisionToObject(obj, true);
    				array[i] = obj;
    				
    				// Create two rows of umbrellas
    				if (i % 2 == 0 )
    				{
    					Obj_SetX(obj,480);
    					Obj_SetAngle(obj,rand(170,190));
    				}
    				if (i % 2 == 1)
    				{
    					Obj_SetX(obj,5);
    					Obj_SetAngle(obj,rand(350,370));
    				}
    			}
    		}
    		
    		// Start spawning bullets after 360 frames
    		if (count > 300)
    		{
    			ascent(i in 0..4)
    			{
    				Raindrop(rand(40,420), 0, rand(4,6), rand(85,95), BLUE11, 60, array);
    			}
    		}
    		
    		// Iterate through the umbrellas. If one has run off a side of the screen,
    		// move it to the other side. Also, might reverse velocity (by rotating it
    		// with Obj_SetAngle ()).
    		ascent(i in 0..7){
    			let obj = array[i];
    			if (Obj_GetX(obj) > 480){
    				Obj_SetAngle(obj, rand(350,370));
    				Obj_SetX(obj, 5);
    				Obj_SetY(obj, rand(100,200));
    			}
    			if (Obj_GetX(obj) < 5){
    				Obj_SetAngle(obj, rand(170,190));
    				Obj_SetX(obj, 450);
    				Obj_SetY(obj, rand(100,200));
    			}
    		}
    		
    		// Increase frame count
    		count++;
    		
    		// Allow other threads to run?
    		yield;
    	}
    	
    	@DrawLoop
    	{
    		SetTexture("script\img\ExRumia.png");
    		DrawGraphic(GetX(),GetY());
    		DrawText("Illusion Sign", GetClipMinX()+20, GetClipMinY()+16, 12, 210);
    	}
    	
    	@Finalize
    	{
    		DeleteGraphic("script\img\ExRumia.png");
    	}
    	
    	// Note: "clouds" is actually an array of umbrellas
    	task Raindrop(x, y, v, angle, graphic, delay, clouds)
    	{
    		let obj = Obj_Create(OBJ_SHOT);
    
    		Obj_SetX(obj,x);
    		Obj_SetY(obj,y);
    		Obj_SetSpeed(obj,v);
    		ObjShot_SetGraphic(obj, graphic);
    		ObjShot_SetDelay(obj,delay);
    		Obj_SetAngle(obj,angle);
    		Obj_SetCollisionToObject(obj, true);
    		
    		while (!Obj_BeDeleted(obj))
    		{
    			// For each raindrop, check if it's collided with an umbrella. If it has, delete it.
    			// 7 = same 7 as in the umbrella creation loop
    			ascent(i in 0..7)
    			{
    				let cloud = clouds[i];
    				if (Collision_Obj_Obj(obj, cloud)){
    					Obj_Delete(obj);
    				}
    			}
    			yield;
    		}
    	}
    }
    

    Janin on
    [SIGPIC][/SIGPIC]
  • Options
    AroducAroduc regular
    edited November 2007
    That's easy to fix. Add in Obj_BombResist(false); (I think that's it) when it defines them. I wonder why I didn't. *shrug*

    The SetCollisionA and SetCollisionB refer to the Main (boss) collision box. A is for player/object collisions and B is for player shots. IE, it takes damage from shots at a larger range than the player can run into it. The umbrella generation doesn't create two rows, it just creates two sets moving in opposite directions. It could be slimmed down to a single ascent loop and then use "(i%2) * 180 + rand(-10,10)" to set the appropriate angles left and right and similar for the horizontal placement. The cloud array should also be generalized by incrementing from 0 to the length of the array passed, but... *shrug* only one of many things that should be cleaned up.

    Aroduc on
  • Options
    GihgehlsGihgehls Registered User regular
    edited November 2007
    This is very interesting stuff and I am reading all of it. Please don't stop until you're done.

    Gihgehls on
    PA-gihgehls-sig.jpg
  • Options
    JaninJanin Registered User regular
    edited November 2007
    Played around some more with the Umbrella/rain script. The number of umbrellas may now be set by a variable, and each variable is monitored in its own task rather than in a giant MainLoop. I think for my first real script, I'll try to do some sort of double-helix bullet path winding around a central core. If I'm thinking right, it will look rather cool.

    I also want to create a pastebin for this stuff at some point, so we don't have to post spoilered code directly into the forums. It might even get syntax highlighting, depending on how hard that is and how bored I am.

    Updated rain script:
    #TouhouDanmakufu
    #Title[Storm Sign &#12300;Rain Cloud&#12301;]
    #Text[Chew on this.]
    #Image[]
    #BackGround[Default]
    #Player[REIMU,MARISA]
    #ScriptVersion[2]
    #BGM[.\bgm\thC15.mp3]
    
    script_enemy_main
    {
    	let frame_count;
    	let umbrella_list = [];
    	let umbrella_count = 7;
     	
    	@Initialize
    	{
    		frame_count = 0;
    		SetLife (2000);
    		SetGraphicRect (64, 1, 127, 64);
    		LoadGraphic ("script\img\ExRumia.png");
    		SetMovePosition02 (GetCenterX, 120, 60);
    		SetScore (38000);
    		SetTimer (30);
    		SetInvincibility (300);
    		CutIn (YOUMU, "Storm Sign &#12300;Rain Cloud&#12301;", 0, 0, 0, 0, 0);
    		SetEnemyMarker (true);
    	}
    	
    	@MainLoop
    	{
    		// Boss collision box
    		// _A sets how far until the player collides with the boss
    		// _B sets how far until bullets collide with the boss
    		SetCollisionA (GetX, GetY, 32);
    		SetCollisionB (GetX, GetY, 24);
    		
    		// Create the umbrellas after 60 frames
    		if (frame_count == 60)
    		{
    			create_umbrellas (umbrella_count);
    		}
    		
    		// Start spawning bullets after 360 frames
    		if (frame_count >= 360)
    		{
    			times (4)
    			{
    				Raindrop (rand (40,420), 0, rand (4,6), rand (85,95),
    				          BLUE11, 60, umbrella_list);
    			}
    		}
    		
    		frame_count++;
    		
    		// Allow other threads to run?
    		yield;
    	}
    	
    	@DrawLoop
    	{
    		SetTexture ("script\img\ExRumia.png");
    		DrawGraphic (GetX (),GetY ());
    		DrawText ("Illusion Sign",
    		          GetClipMinX () + 20, GetClipMinY () + 16,
    		          12, 210);
    	}
    	
    	@Finalize
    	{
    		DeleteGraphic ("script\img\ExRumia.png");
    	}
    
    	function create_umbrellas (count)
    	{
    		ascent (ii in 0 .. count)
    		{
    			let umbrella = Obj_Create (OBJ_SHOT);
    			
    			Obj_SetY (umbrella, rand (100,200));
    			Obj_SetSpeed (umbrella, rand (1,2));
    			ObjShot_SetGraphic(umbrella, WHITE03);
    			ObjShot_SetDelay (umbrella, ii * 20);
    			Obj_SetCollisionToObject (umbrella, true);
    			ObjShot_SetBombResist (umbrella, true);
    			
    			// Create two rows of umbrellas
    			if (ii &#37; 2 == 0)
    			{
    				Obj_SetX (umbrella, 480);
    				Obj_SetAngle (umbrella, rand (170,190));
    			}
    			
    			else
    			{
    				Obj_SetX (umbrella, 5);
    				Obj_SetAngle (umbrella, rand (350,370));
    			}
    			
    			monitor_umbrella (umbrella);
    			umbrella_list = umbrella_list ~ [umbrella];
    		}
    	}
    	
    	task monitor_umbrella (umbrella)
    	{
    		loop
    		{
    			// Check if an umbrella has run off the side of the screen.
    			// If it has, move it to the other side and reverse its velocity.
    			if (Obj_GetX (umbrella) > 480)
    			{
    				Obj_SetAngle (umbrella, rand (350, 370));
    				Obj_SetX (umbrella, 5);
    				Obj_SetY (umbrella, rand (100, 200));
    			}
    			if (Obj_GetX (umbrella) < 5)
    			{
    				Obj_SetAngle (umbrella, rand (170, 190));
    				Obj_SetX (umbrella, 450);
    				Obj_SetY (umbrella, rand (100, 200));
    			}
    			
    			yield;
    		}
    	}
    	
    	task Raindrop(x, y, v, angle, graphic, delay, umbrellas)
    	{
    		let drop = Obj_Create(OBJ_SHOT);
    
    		Obj_SetX (drop, x);
    		Obj_SetY (drop, y);
    		Obj_SetSpeed (drop, v);
    		ObjShot_SetGraphic (drop, graphic);
    		ObjShot_SetDelay (drop, delay);
    		Obj_SetAngle (drop, angle);
    		Obj_SetCollisionToObject (drop, true);
    		
    		while (!Obj_BeDeleted (drop))
    		{
    			// For each raindrop, check if it's collided with an umbrella
    			// Delete any drops that have.
    			ascent(ii in 0 .. length (umbrellas))
    			{
    				let umbrella = umbrellas[ii];
    				if (Collision_Obj_Obj (drop, umbrella))
    				{
    					Obj_Delete (drop);
    				}
    			}
    			yield;
    		}
    	}
    }
    

    Janin on
    [SIGPIC][/SIGPIC]
  • Options
    AroducAroduc regular
    edited November 2007
    Added some shots to the terminus of the lightning. And music and sounds.

    Whee.
    #東方弾幕風
    #Title[Storm Sign 「Zeus' Fury 」]
    #Text[Zzzap.]
    #Image[]
    #BackGround[Default]
    #Player[REIMU,MARISA]
    #ScriptVersion[2]
    #BGM[.\bgm\thC07.mp3]

    script_enemy_main
    {
    let count;


    @Initialize
    {
    count=-60;
    SetLife(2000);
    SetGraphicRect(64,1,127,64);
    LoadGraphic("script\img\ExRumia.png");
    SetMovePosition02(GetCenterX, 120, 60);
    SetScore(38000);
    SetMovePosition02(GetCenterX, 120, 60);
    SetTimer(50);
    SetInvincibility(120);
    CutIn(YOUMU,"Storm Sign 「Zeus' Fury」", 0, 0, 0, 0, 0);
    SetEnemyMarker(true);
    LoadSE("se\bom20_c.wav");
    }

    @MainLoop
    {
    loop(10){PuffyCloud();}

    if (count % 12 == 0){HorizLightning(rand(GetClipMinX, GetClipMaxX), rand(GetClipMinY+80,GetClipMinY), rand(0,360));}

    if (count % 150 == 0){SetMovePositionRandom01(24, 12, 3, GetClipMinX + 32, GetClipMinY + 64, GetClipMaxX - 32, GetClipMinY + 96);}

    SetCollisionA(GetX, GetY, 32);
    SetCollisionB(GetX, GetY, 24);
    count++;
    yield;
    }

    @DrawLoop
    {
    SetTexture("script\img\ExRumia.png");
    DrawGraphic(GetX(),GetY());
    DrawText("Weather Witch", GetClipMinX+20, GetClipMinY+16, 12, 210);
    }

    @Finalize
    {
    DeleteGraphic("script\img\ExRumia.png");
    }

    task PuffyCloud()
    {
    let obj = Obj_Create(OBJ_SHOT);
    let count = 0;

    Obj_SetPosition(obj,rand(GetClipMinX, GetClipMaxX), rand(GetClipMinY+100,GetClipMinY));
    Obj_SetSpeed(obj,rand(0.25,0.75));
    ObjShot_SetGraphic(obj, WHITE04);
    Obj_SetAngle(obj, rand(0,360));
    Obj_SetAlpha(obj, rand(25,50));
    Obj_SetCollisionToPlayer(obj, false);

    while (count <= 120) { count++; yield; }
    ObjShot_FadeDelete (obj);
    }

    task HorizLightning(x, y, angle)
    {
    let obj = Obj_Create(OBJ_LASER);
    let count = 0;

    Obj_SetPosition(obj, x, y);
    ObjShot_SetGraphic(obj, BLUE05);
    ObjLaser_SetWidth(obj, 5);
    ObjLaser_SetLength(obj, rand(40,50));
    Obj_SetAngle(obj,angle);
    ObjLaser_SetSource(obj,false);
    ObjShot_SetDelay(obj, 5);


    while (!Obj_BeDeleted(obj)){

    if (ObjLaser_GetEndX(obj) < GetClipMinX + 32){angle = angle-180;}
    if (ObjLaser_GetEndX(obj) > GetClipMaxX - 32){angle = angle-180;}

    if (ObjLaser_GetEndY(obj) > GetClipMinY + 100){
    if (count < 10){
    CreateShotA(1, ObjLaser_GetEndX(obj), ObjLaser_GetEndY(obj), 60);
    SetShotKillTime(1, 0);
    FireShot(1);count = 15;
    }

    if (count == 45){
    VertLightning(ObjLaser_GetEndX(obj) - 10*cos(angle), ObjLaser_GetEndY(obj)-20, rand(60,120));
    PlaySE("se\bom20_c.wav");
    Obj_Delete(obj);
    }
    }
    else if (count == 5){
    HorizLightning(ObjLaser_GetEndX(obj), ObjLaser_GetEndY(obj), rand(angle-15,angle+15));
    }
    else if (count == 10){Obj_Delete(obj);}




    count++;
    yield;
    }
    }

    task VertLightning(x, y, angle)
    {
    let obj = Obj_Create(OBJ_LASER);
    let count = 0;
    let newAngle = rand(60,120);

    Obj_SetPosition(obj, x, y);
    ObjShot_SetGraphic(obj, BLUE05);
    ObjLaser_SetWidth(obj, 20);
    ObjLaser_SetLength(obj, rand(40,50));
    Obj_SetAngle(obj,angle);
    ObjLaser_SetSource(obj,false);
    ObjShot_SetDelay(obj, 35);


    while (!Obj_BeDeleted(obj)){

    if (ObjLaser_GetEndY(obj) > GetClipMaxY){
    ascent(i in 0..7){CreateShot01(ObjLaser_GetEndX(obj), GetClipMaxY-8, 1, 180+(30*i)+rand(-3,3), BLUE05, 30);}
    Obj_Delete(obj);
    }

    if (count == 1){
    if (rand_int(0,10) == 5){
    VertLightning(ObjLaser_GetEndX(obj) - 10*cos(angle), ObjLaser_GetEndY(obj)-20, rand(30,150));
    }
    VertLightning(ObjLaser_GetEndX(obj) - 10*cos(angle), ObjLaser_GetEndY(obj)-20, rand(60,120));

    }

    if (count == 45){Obj_Delete(obj);}

    count++;
    yield;
    }
    }
    }

    Everything in action (well, some nasty slowdown, my laptop is weak)
    http://www.youtube.com/watch?v=QStcs_K5pdo

    Whee.

    Aroduc on
  • Options
    AiranAiran Registered User regular
    edited November 2007
    Your first 2 videos are broken. ToU violation or something.

    The code looks absolutely terrfying at first, but after rubbing my eyes it seems to make slight sense. I'm gonna download the mega pack and see what I can pick apart from this.

    Airan on
    paDudSig.jpg
  • Options
    JaninJanin Registered User regular
    edited November 2007
    Airan wrote: »
    Your first 2 videos are broken. ToU violation or something.

    The code looks absolutely terrfying at first, but after rubbing my eyes it seems to make slight sense. I'm gonna download the mega pack and see what I can pick apart from this.

    The language is rather weird. It's like a horrible mixture of C and Java, and I can't find much documentation on it aside from the half-translated API reference on Pooshlmer. Some of the operators are weird also - for example, the tilde (~) is array concatenation and \" is a complete string. I also find stuff like the LoadGraphic/SetTexture/DrawGraphic puzzling - why not simply have a Graphic object created in the same way as shots or lasers?

    Janin on
    [SIGPIC][/SIGPIC]
  • Options
    AiranAiran Registered User regular
    edited November 2007
    Tried loading up the script text files in Notepad++, but the JP characters ended up not being displayed properly. Had to resave them as Unicode files o_O

    Any editor recommendations? I'm not particularly fond of notepad for programming (plus the Shift-JIS encoding makes the font all tiny).

    Airan on
    paDudSig.jpg
  • Options
    JaninJanin Registered User regular
    edited November 2007
    I used gvim, since it's a nice editor and supports Shift_JIS natively. If you don't want to bother with encoding, just write the whole thing in ASCII and skip the 「funny quotes」.

    Janin on
    [SIGPIC][/SIGPIC]
  • Options
    AroducAroduc regular
    edited November 2007
    Noooooooo not the funny quotes!

    And here are a couple replacement videos
    http://www.youtube.com/watch?v=dxb34S4pWz0
    http://www.youtube.com/watch?v=7teMsb5FQKU

    I assume you can't read Japanese, so the Japanese documentation probably will do no good. I've come across a few tutorials and whatnot for it though I've (slowly) been sort of quasi-translating. At the moment though, they mostly consist of my notes saying what the tutorials are. I get distracted easily. It is a little weird though since the tutorials tend towards the more western (as I've noted) style of programming instead of all the weird segregated threads that I've seen on the eastern ones.

    Aroduc on
  • Options
    AiranAiran Registered User regular
    edited November 2007
    I've read that gVim is a manly text editor, and it appears to be true. I can't even find the settings to change the background colour to black and text white/grey D:

    Airan on
    paDudSig.jpg
  • Options
    JaninJanin Registered User regular
    edited November 2007
    Airan wrote: »
    I've read that gVim is a manly text editor, and it appears to be true. I can't even find the settings to change the background colour to black and text white/grey D:

    You don't have to actually use it for editing, just conversion back and forth between UTF-8 and Shift_JIS. Here's an example configuration for setting background color. That strange feeling you're getting is hair growing on your chest.

    Janin on
    [SIGPIC][/SIGPIC]
Sign In or Register to comment.