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-Uv0http://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.
Where to get shitDanmakufu itselfhttp://www.geocities.co.jp/SiliconValley-Oakland/9951/products/th_dnh.html (v.12m... yes, it's only 4 mb)
English Documentationhttp://pooshlmer.com/touhouwiki/index.php/DanmakufuJapanese Documentationhttp://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 thingshttp://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.
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:
#東方弾幕風
#Title[Storm Sign 「Rain Cloud」]
#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 「Rain Cloud」", 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:
#東方弾幕風
#Title[Hive Sign 「Wrath of the Swarm」]
#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 「Wrath of the Swarm」", 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 「Gravity Bomb」", 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
Posts
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.
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.
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.
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:
rather than simply:
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?
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.
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:
Using this image
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)
with whatever you wanted though.
In case Aroduc and I are not the only people in the world reading this thread, here is the commented code:
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.
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:
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.
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?
Any editor recommendations? I'm not particularly fond of notepad for programming (plus the Shift-JIS encoding makes the font all tiny).
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.
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.