Heres another little something Ive put together, a LIGHTING BOLT generator.

Ever needed a genuinely random, 3D lightning-strike from point A to boint B?
With variable thickness, color, and complexity? And with a cool glowing after-image? (retina-burn)
Ever wanted that "beautiful" bolt to be repeatable at a later time?

Now you can with a single function call.

Comes as a complete DEMO main.c with no necessary additional files. Plenty of included documentation.

Once again, any suggestions, questions, or instructional issues, just let me know...

[EDIT] NOW BUGFIXED FOR A7 Ver 7.82.2 !!

Code:
#include <acknex.h>
#include <default.c>
//
//
//////////////////////////////////////////////////////////////////////////////////////////
//	3D-LIGHTNING-BOLT-SPAWN	2.1															//
//	---------------------------															//
//																						//
//	Author	: EvilSOB																	//
//	Date	: 31-07-2009																//
//	Updated	: 06-04-2010	(7.82.2 bugfix)												//
//																						//
//////////////////////////////////////////////////////////////////////////////////////////
//LB_spawn(VECTOR*Start,VECTOR*Finish,Div,Branch,Depth,Size,COLOR*Core,COLOR*Flare,FlareTime,Seed)//
//--------------------------------------------------------------------------------------//
//	Parameters::																		//
//		Start	=	Start Vector of Lightning Bolt 	(sky)								//
//		Finish	=	Target Vector of Lightning Bolt	(ground)							//
//		Div		=	Number of primary "segments" in central bolt 		 (suggest 5->20)//
//		Branch	=	0.0->1.0 chance to "branch" at primary segment point.(suggest 0.4)	//
//		Depth	=	depth of complexity to calculate into branches.		 (suggest 4)	//
//		Size	=	diameter in quants of "core" bolt.	 				 (suggest 8)	//
//		Core	=	color of lightning "bolt" 				   	   (suggest 255,128,128)//
//		Flare	=	color of lightning-bolt 'afterimage'	   	   (suggest 128,  0,  0)//
//		FlareTime=	duration in seconds of 'afterimage'					 (suggest 1.5)	//
//		Seed	=	random number seed (for forcing duplicates)			 (suggest 0)	//
//																						//
//	NOTES : "Flare" = NULL or nullvector will suppress the "afterimage" generation.		//
//			"Seed"  = Zero is random, set to "any" number will generate duplicate stikes//
//			"Div"   = !Be cautious with Divisions above 20, it can be a real FPS eater!	//
//			"Depth" = !Be cautious with the depth above 3, it can be a real FPS eater!	//
//																						//
//////////////////////////////////////////////////////////////////////////////////////////
//	Example::																			//
//		LB_spawn(me.x, you.x, 15, 0.4, 4, 8, vector(255,128,128), vector(180,0,0), 0);	//
//																						//
//////////////////////////////////////////////////////////////////////////////////////////
//																						//
//
void LB_segment_fade(PARTICLE* p)	{  	p.lifespan = p.alpha -= time_step*p.skill_x; 	}
//
void LB_segment_core(PARTICLE* p)
{	vec_set(p.blue, 	you.skill3);
	set(p, BEAM|BRIGHT|TRANSLUCENT|UNLIT|CAST);
	p.alpha   = 100;
	p.size    = you.skill2;
	p.skill_x = 20;
	p.event   = LB_segment_fade;
}
//
void LB_segment_flare(PARTICLE* p)
{	vec_set(p.blue, 	you.skill6);
	set(p, BEAM|BRIGHT|TRANSLUCENT|UNLIT|CAST);
	p.alpha   = 20;
	p.size    = you.skill2*2;
	p.skill_x = 1.28/you.skill7;
	p.event   = LB_segment_fade;
}
//
void LB_divide(VECTOR* A, VECTOR* B, long Div, float noise, VECTOR** Points)
{	VECTOR *points = (VECTOR*)malloc(sizeof(VECTOR)*Div);
	if(!Points)		free(*Points);		*Points = points;
	var idx; for(idx=0; idx<Div; idx++)	
	{	vec_lerp(points[idx], A, B, (float)idx/(Div-1));
		if((idx>0)&&(idx<(Div-1)))	
		{	points[idx].x += random(noise*2)-noise;
			points[idx].y += random(noise*2)-noise;
			points[idx].z += random(noise)-noise/2;
}	}	}
//
//
void LB_spawn(VECTOR* Start, VECTOR* Finish, long Div, float Branch, long Depth, long Size, COLOR* Core, COLOR* Flare, float FlareTime, var Seed)
{	if(Div<3)	return;
	if(!Seed)	Seed = timer();	
	VECTOR A, B, C, D, tmpV, corec, flarec, *fine=0, *crude=0;	vec_zero(flarec);
	vec_set(A, Start);		vec_set(B, Finish);		vec_set(corec, Core);	
	if(Flare)	vec_set(flarec, Flare);		
	wait(1);	
	long i, cnt, seg_size = vec_dist(A,B)/Div;		random_seed(Seed);
	LB_divide(A, B, Div, vec_dist(A,B)/(Div+1), crude);
	me = ent_create(NULL, NULL, NULL);		me.skill2 = Size;		vec_set(me.skill3, corec);
	vec_set(me.skill6, flarec);				me.skill7 = FlareTime;	
	for(i=1; i<Div; i++)	
	{	LB_divide(crude[i-1],crude[i],Div/2,vec_dist(crude[i-1],crude[i])/Div/2+1,fine);
		for(cnt=1; cnt<(Div/2); cnt++)
		{	vec_set(tmpV, fine[cnt]);	vec_sub(tmpV, fine[cnt-1]);
			if(vec_length(tmpV))
				vec_scale(tmpV, vec_length(tmpV)/vec_length(tmpV));
			if(vec_length(flarec))	effect(LB_segment_flare, 1, fine[cnt-1], tmpV);
			effect(LB_segment_core, 1, fine[cnt-1], tmpV);				}	
		if((Depth>1)&&(random(1)<Branch)&&(i<(Div-1)))
		{	vec_diff(C, crude[i], crude[i-1]);
			vec_diff(D, crude[i], crude[i+1]);
			vec_lerp(tmpV, D, C, random(1)+0.35);
			vec_normalize(tmpV, random(seg_size*(Div-i))*0.6);	vec_add(tmpV,crude[i]); 
			LB_spawn(crude[i], tmpV, Div*0.6, Branch, Depth-1, Size*0.6, corec, flarec, FlareTime, Seed+random(15));
	}	}
	wait(1);	ent_remove(me);		free(crude);	free(fine);
}
//
//
//////////////////////////////////////////////////////////////////////////////////////////
//
//







//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
//				Remaining code is for DEMO purposes only.								//
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
//
//
VECTOR FromPoint, ToPoint;
var strike()	{	LB_spawn(FromPoint, ToPoint, 20, 0.7, 7, 8, vector(255,128,128), vector(180,0,0), 1.5, 00);  }	
var arcing()		
{	var last_key = key_lastpressed;
	while(key_pressed(last_key))
	{		LB_spawn(FromPoint, ToPoint, 13, 0.6, 3, 13, vector(255,128,128), vector(128,0,0), 1, 0);	
			wait(-0.07);
}	}
//
//
function main()
{	max_entities = 10000;
	
	level_load(NULL);	wait(2);	diag("\n\n\n");
	vec_set(sky_color, vector(1,1,1));
	vec_set(camera.x, vector(-700,0,255));
	//
	vec_set(FromPoint, vector(0,0,500));		
	vec_set(ToPoint,   vector(0,100,0));
	on_space = strike;
	on_enter = arcing;
	//
	//
	strike();
	while(1)
	{
		draw_text(_str("Hit SPACEBAR for Lightning Strike!"), 10, 10, NULL);
		draw_text(_str("Hold ENTER for an Elecrical Arc!"),   10, 30, NULL);
		draw_point3d(FromPoint,  vector(255,200,200), 100, 10);
		draw_point3d(ToPoint,    vector(255,200,200), 100, 10);

		wait(1);
	}
	//
	sys_exit("");
}




For historical purposes, here is Version 2.0. Its the same as 2.1, just doesnt allow control over the color of the after-image 'flare'.
Click to reveal..
Code:
#include <acknex.h>
#include <default.c>
//
//
//////////////////////////////////////////////////////////////////////////////////////////
//	3D-LIGHTNING-BOLT-SPAWN	2.0															//
//	---------------------------															//
//																						//
//	Author	: EvilSOB																	//
//	Date	: 31-07-2009																//
//	Updated	: 06-04-2010	(7.82.2 bugfix)												//
//																						//
//////////////////////////////////////////////////////////////////////////////////////////
//LB_spawn(VECTOR*Start,VECTOR*Finish,Div,Branch,Depth,Size,COLOR*Core,COLOR*Flare,Seed)//
//--------------------------------------------------------------------------------------//
//	Parameters::																		//
//		Start	=	Start Vector of Lightning Bolt 	(sky)								//
//		Finish	=	Target Vector of Lightning Bolt	(ground)							//
//		Div		=	Number of primary "segments" in central bolt 		 (suggest 5->20)//
//		Branch	=	0.0->1.0 chance to "branch" at primary segment point.(suggest 0.4)	//
//		Depth	=	depth of complexity to calculate into branches.		 (suggest 4)	//
//		Size	=	diameter in quants of "core" bolt.	 				 (suggest 8)	//
//		Core	=	color of lightning "bolt" 				   	   (suggest 255,128,128)//
//		Flare	=	color of lightning-bolt 'afterimage'	   	   (suggest 128,  0,  0)//
//		Seed	=	random number seed (for forcing duplicates)			 (suggest 0)	//
//																						//
//	NOTES : "Flare" = NULL or nullvector will suppress the "afterimage" generation.		//
//			"Seed"  = Zero is random, set to "any" number will generate duplicate stikes//
//			"Div"   = !Be cautious with Divisions above 20, it can be a real FPS eater!	//
//			"Depth" = !Be cautious with the depth above 3, it can be a real FPS eater!	//
//																						//
//////////////////////////////////////////////////////////////////////////////////////////
//	Example::																			//
//		LB_spawn(me.x, you.x, 15, 0.4, 4, 8, vector(255,128,128), vector(180,0,0), 0);	//
//																						//
//////////////////////////////////////////////////////////////////////////////////////////
//																						//
//
void LB_segment_fade(PARTICLE* p)	{  	p.lifespan = p.alpha -= time_step*p.skill_x;	}
//
void LB_segment_core(PARTICLE* p)
{	vec_set(p.blue, 	you.skill3);
	set(p, BEAM|BRIGHT|TRANSLUCENT|UNLIT|CAST);
	p.alpha   = 100;
	p.size    = you.skill2;
	p.skill_x = 20;
	p.event   = LB_segment_fade;
}
//
void LB_segment_flare(PARTICLE* p)
{	vec_set(p.blue, 	you.skill6);
	set(p, BEAM|BRIGHT|TRANSLUCENT|UNLIT|CAST);
	p.alpha   = 20;
	p.size    = you.skill2*2;
	p.skill_x = 1;
	p.event   = LB_segment_fade;
}
//
void LB_divide(VECTOR* A, VECTOR* B, long Div, float noise, VECTOR** Points)
{	VECTOR *points = (VECTOR*)malloc(sizeof(VECTOR)*Div);
	if(!Points)		free(*Points);		*Points = points;
	var idx; for(idx=0; idx<Div; idx++)	
	{	vec_lerp(points[idx], A, B, (float)idx/(Div-1));
		if((idx>0)&&(idx<(Div-1)))	
		{	points[idx].x += random(noise*2)-noise;
			points[idx].y += random(noise*2)-noise;
			points[idx].z += random(noise)-noise/2;
}	}	}
//
//
void LB_spawn(VECTOR* Start, VECTOR* Finish, long Div, float Branch, long Depth, long Size, COLOR* Core, COLOR* Flare, var Seed)
{	if(Div<3)	return;
	if(!Seed)	Seed = timer();	
	VECTOR A, B, C, D, tmpV, corec, flarec, *fine=0, *crude=0;	vec_zero(flarec);
	vec_set(A, Start);		vec_set(B, Finish);		vec_set(corec, Core);	
	if(Flare)	vec_set(flarec, Flare);
	wait(1);	
	long i, cnt, seg_size = vec_dist(A,B)/Div;		random_seed(Seed);
	LB_divide(A, B, Div, vec_dist(A,B)/(Div+1), crude);
	me = ent_create(NULL, NULL, NULL);		me.skill2 = Size;
	vec_set(me.skill3, corec);				vec_set(me.skill6, flarec);
	for(i=1; i<Div; i++)	
	{	LB_divide(crude[i-1],crude[i],Div/2,vec_dist(crude[i-1],crude[i])/Div/2+1,fine);
		for(cnt=1; cnt<(Div/2); cnt++)
		{	vec_set(tmpV, fine[cnt]);	vec_sub(tmpV, fine[cnt-1]);
			if(vec_length(tmpV))
				vec_scale(tmpV, vec_length(tmpV)/vec_length(tmpV));
			if(vec_length(flarec))	effect(LB_segment_flare, 1, fine[cnt-1], tmpV);
			effect(LB_segment_core, 1, fine[cnt-1], tmpV);				}	
		if((Depth>1)&&(random(1)<Branch)&&(i<(Div-1)))
		{	vec_diff(C, crude[i], crude[i-1]);
			vec_diff(D, crude[i], crude[i+1]);
			vec_lerp(tmpV, D, C, random(1)+0.35);
			vec_normalize(tmpV, random(seg_size*(Div-i))*0.6);	vec_add(tmpV,crude[i]); 
			LB_spawn(crude[i], tmpV,Div*0.6,Branch,Depth-1,Size*0.6, corec, flarec, Seed+random(15));
	}	}
	wait(1);	ent_remove(me);		free(crude);	free(fine);
}
//
//
//////////////////////////////////////////////////////////////////////////////////////////
//
//







//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
//				Remaining code is for DEMO purposes only.								//
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
//
//
VECTOR FromPoint, ToPoint;
var strike()	{	LB_spawn(FromPoint, ToPoint, 20, 0.4, 5, 8, vector(255,128,128), vector(180,0,0), 0);	}	
var arcing()		
{	var last_key = key_lastpressed;
	while(key_pressed(last_key))
	{		LB_spawn(FromPoint, ToPoint, 10, 0.4, 2, 10, vector(255,128,128), vector(180,0,0), 0);	
			wait(-0.07);
}	}
//
//
function main()
{
	warn_level = 6;

	level_load(NULL);	wait(2);	diag("\n\n\n");
	vec_set(sky_color, vector(1,1,1));
	vec_set(camera.x, vector(-700,0,255));
	//
	vec_set(FromPoint, vector(0,0,500));		
	vec_set(ToPoint,   vector(0,100,0));
	on_space = strike;
	on_1 = arcing;

	//
	//
	strike();
	while(1)
	{
		draw_text(_str("Hit SPACEBAR for Lightning Strike!"), 10, 10, NULL);
		draw_point3d(FromPoint,  vector(255,200,200), 100, 10);
		draw_point3d(ToPoint,    vector(255,200,200), 100, 10);
		wait(1);
	}
	//
	sys_exit("");
}




Also historical purposes, here is Ye-Olde Version 1.0. Much simpler code.
Its basically the same as 2.0, but doesnt allow ANY control over the after-image 'flare'.
Click to reveal..
Code:
#include <acknex.h>
#include <default.c>
//
//
//////////////////////////////////////////////////////////////////////////////////////////
//	3D-LIGHTNING-BOLT-SPAWN	1.0															//
//	---------------------------															//
//																						//
//	Author	: EvilSOB																	//
//	Date	: 25-07-2009																//
//																						//
//////////////////////////////////////////////////////////////////////////////////////////
//LB_spawn(VECTOR* start, VECTOR* finish, long Div, float branch, long depth, long size)//
//--------------------------------------------------------------------------------------//
//	Parameters::																		//
//		start	=	Start Vector of Lightning Bolt 	(sky)								//
//		finish	=	Target Vector of Lightning Bolt	(ground)							//
//		Div		=	Number of primary "segments" in central bolt 		  (suggest 20)	//
//		branch	=	0.0->1.0 chance to "branch" at primary segment point. (suggest 0.4)	//
//		depth	=	depth of complexity to calculate into branches.		  (suggest 4)	//
//		size	=	diameter in quants of "core" bolt.(2^depth minimum)	  (suggest 8)	//
//																						//
//////////////////////////////////////////////////////////////////////////////////////////
//	Example::																			//
//		LB_spawn(start, finish, 20, 0.4, 4, 8);											//
//																						//
//////////////////////////////////////////////////////////////////////////////////////////
//																						//
void LB_segment_fade(PARTICLE* p)
{  	p.alpha -= 20 * p.skill_a * time_step;
    if (p.alpha <= 0)	p.lifespan = 0;
}
//
void LB_segment_flare(PARTICLE* p)
{	vec_set(p.blue, vector(128,0,0));
	set(p, BEAM|BRIGHT|TRANSLUCENT|UNLIT|CAST);
	p.alpha = 50;
	p.size = (long)you*3;
	p.skill_a = 0.3;
	p.event = LB_segment_fade;
}
//
void LB_segment_core(PARTICLE* p)
{	vec_set(p.blue, vector(255,128,128));
	set(p, BEAM|BRIGHT|TRANSLUCENT|UNLIT|CAST);
	p.alpha = 100;
	p.size = (long)you;
	p.skill_a = 1.5;
	p.event = LB_segment_fade;
}
//
void LB_draw_segment(VECTOR *start, VECTOR *finish, long size)
{	me = size;
	VECTOR tmpV;	vec_set(tmpV, finish);	vec_sub(tmpV,start);
	effect(LB_segment_core,  5, start, tmpV);
	effect(LB_segment_flare, 1, start, tmpV);
	me = size;
}
//
//
void LB_randomize(VECTOR* point, float noise)
{
	point.x += random(noise*2)-noise;
	point.y += random(noise*2)-noise;
	point.z += random(noise)-noise/2;
}
//
void LB_subdivide(VECTOR* A, VECTOR* B, long Div, float noise, VECTOR** Points)
{	VECTOR *points = (VECTOR*)malloc(sizeof(VECTOR)*Div);
	if(!Points)		free(*Points);		*Points = points;
	var idx; for(idx=0; idx<Div; idx++)	
	{	vec_lerp(points[idx], A, B, (float)idx/(Div-1));
		if((idx>0)&&(idx<(Div-1)))	LB_randomize(points[idx], noise);
	}
}
//
void LB_spawn(VECTOR* start,VECTOR* finish,long Div,float branch,long depth,long size)
{	VECTOR A, B;	vec_set(A, start);	vec_set(B, finish);		wait(1);
	long i, cnt, seg_size=vec_dist(A,B)/Div;
	VECTOR *fine=0, 	*crude=0;	LB_subdivide(A, B, Div, vec_dist(A,B)/(Div+1), crude);
	for(i=1; i<Div; i++)	
	{	LB_subdivide(crude[i-1],crude[i],Div/2,vec_dist(crude[i-1],crude[i])/Div/2+1,fine);
		for(cnt=1; cnt<(Div/2); cnt++)	LB_draw_segment(fine[cnt-1], fine[cnt], size);
		if((depth>1)&&(random(1)<branch))
		{	VECTOR tmpV;	vec_set(tmpV,crude[i]);	  LB_randomize(tmpV, vec_dist(A,B)/5);
			LB_spawn(crude[i-1], tmpV, Div/2, branch, depth-1, size/2);
	}	}
	free(crude);	free(fine);	
}
//////////////////////////////////////////////////////////////////////////////////////////
//
//
VECTOR start, finish;
void strike()	{	LB_spawn(start, finish, 20, 0.4, 4, 8);		}
//
//
function main()
{
	level_load(NULL);	wait(2);	diag("\n\n\n");
	vec_set(sky_color, vector(1,1,1));
	vec_set(camera.x, vector(-700,0,255));
	//
	on_space = strike;
	//
	vec_set(start, vector(0,0,500));		
	vec_set(finish, vector(0,100,0));
	LB_spawn(start, finish, 20, 0.4, 4, 8);	
	//
	while(1)
	{
		draw_point3d(start,  vector(255,200,200), 100, 10);
		draw_point3d(finish, vector(255,200,200), 100, 10);
		wait(1);
	}
}




Last edited by EvilSOB; 04/05/10 15:10.

"There is no fate but what WE make." - CEO Cyberdyne Systems Corp.
A8.30.5 Commercial