function wait(n){loop(n){yield;}}


function speak(top, msg, r, g, b){
    let objText = ObjText_Create();
	ObjText_SetText(objText, msg);
	ObjText_SetFontSize(objText, 20);
	ObjText_SetFontType(objText, "MS Gothic");
	ObjText_SetFontColorTop(objText, r, g, b);
	ObjText_SetFontColorBottom(objText, r, g, b);
	ObjText_SetFontBold(objText, true);
    ObjText_SetHorizontalAlignment(objText, ALIGNMENT_CENTER);
	ObjText_SetMaxWidth(objText, 320);
	let y = 300; if(top){y = 200;}
	ObjRender_SetPosition(objText, 222-160, y,1);
	Obj_SetRenderPriorityI(objText, 79);
	return objText;
}

task skip(objBoss){
	let objText = ObjText_Create();
	ObjText_SetText(objText, "(Press the shot button to skip dialogue)");
	ObjText_SetFontSize(objText, 16);
	ObjText_SetFontType(objText, "Arial");
	ObjText_SetFontColorTop(objText, 255, 255, 255);
	ObjText_SetFontColorBottom(objText, 255, 255, 255);
	ObjText_SetFontBold(objText, false);
    ObjText_SetHorizontalAlignment(objText, ALIGNMENT_CENTER);
	ObjText_SetMaxWidth(objText, 320);
	ObjRender_SetPosition(objText, 222-160, 400,1);
	Obj_SetRenderPriorityI(objText, 79);
	while(ObjEnemy_GetInfo(objBoss, INFO_LIFE)>0){yield;}
	Obj_Delete(objText);
}








// Name functions, enemy indicators.

task Name(num, objBoss){
	let objText = ObjText_Create();
	if(num==1){ObjText_SetText(objText, "Dr Saul Schleimer");}
	else{ObjText_SetText(objText, "DUS Dave Wood");}
	ObjText_SetFontSize(objText, 15);
	ObjText_SetFontType(objText, "Arial");
	ObjText_SetFontBold(objText, true);
	ObjText_SetFontColorTop(objText, 255, 255, 255);
	ObjText_SetFontColorBottom(objText, 255, 255, 255);
		
	ObjText_SetMaxWidth(objText, 150);
	Obj_SetRenderPriorityI(objText, 70);
	ObjRender_SetPosition(objText, 5, 15, 1);
    while(ObjEnemy_GetInfo(objBoss, INFO_LIFE)>0){yield;}
    Obj_Delete(objText);	
}

task Indicator(obj, num){
    let objText = ObjText_Create();
	if(num ==1){ObjText_SetText(objText, "Saul");}
	else if(num ==2){ObjText_SetText(objText, "Dave");}
	ObjText_SetFontSize(objText, 18);
	ObjText_SetFontType(objText, "MS Gothic");
	ObjText_SetFontBold(objText, true);
	ObjText_SetFontColorTop(objText, 255, 254, 0);
	ObjText_SetFontColorBottom(objText, 255, 254, 0);
		
	ObjText_SetMaxWidth(objText, 100);
	ObjText_SetHorizontalAlignment(objText, ALIGNMENT_CENTER);
	Obj_SetRenderPriorityI(objText, 90);
	while(ObjEnemy_GetInfo(obj, INFO_LIFE)>0){
		ObjRender_SetPosition(objText, ObjMove_GetX(obj) - 10, 462, 1);
		yield;
	}
	Obj_Delete(objText);
}


task takeDamage(enemy, scene, lifeB){
    let sound = LoadEffect(GetCurrentScriptDirectory~ "./../SFX/DamageNon.WAV", 90);
	let sound2 = LoadEffect(GetCurrentScriptDirectory~ "./../SFX/DamageSpell.WAV", 90);
    let life; let lifen;
    while(ObjEnemy_GetInfo(enemy, INFO_LIFE)>0){
	    life = ObjEnemy_GetInfo(enemy, INFO_LIFE);
		yield;
		lifen = ObjEnemy_GetInfo(enemy, INFO_LIFE);
		if(lifen< life){
			while(lifen<life){
				if(life<lifeB/8){
				    if(ObjEnemyBossScene_GetInfo(scene, INFO_IS_SPELL)){ObjSound_Play(sound2);}
					else{ObjSound_Play(sound);}
				}
			    life = lifen;
				wait(4);
				lifen = ObjEnemy_GetInfo(enemy,INFO_LIFE);
			}
		}
	}
}










// Spell attacks.

task Spell(name, objBoss, numBoss){
    ObjSound_Play(LoadEffect(GetCurrentScriptDirectory~ "./../SFX/SPELLCARDNEW.WAV", 100));
    SpellPicture(numBoss);
	SpellWarn(numBoss);
	SpellName(name, objBoss);
}


task SpellPicture(numBoss){
    if(numBoss ==1){
		let im = GetCurrentScriptDirectory~ "./../img/spellPicture.png";
		let obj = ObjPrim_Create(OBJ_SPRITE_2D);
		ObjPrim_SetTexture(obj, im);
		ObjSprite2D_SetSourceRect(obj, 80, 245, 415, 800);
		ObjSprite2D_SetDestRect(obj, 0,0, 170,300);
		Obj_SetRenderPriorityI(obj, 70);
		ObjRender_SetPosition(obj, 650, 88, 1);
		while(ObjRender_GetX(obj)>180){
			ObjRender_SetX(obj, ObjRender_GetX(obj) -30);
			yield;
		}
		loop(80){
			ObjRender_SetX(obj, ObjRender_GetX(obj) -1);
			yield;
		}
		loop(30){
			ObjRender_SetX(obj, ObjRender_GetX(obj) -30);
			yield;
		}
	}
	else{
	    let im = GetCurrentScriptDirectory~ "./../img/NonSpellDave.png";
		let obj = ObjPrim_Create(OBJ_SPRITE_2D);
		ObjPrim_SetTexture(obj, im);
		ObjSprite2D_SetSourceRect(obj, 75, 585, 255, 885);
		ObjSprite2D_SetDestRect(obj, 0,0, 200,300);
		Obj_SetRenderPriorityI(obj, 70);
		ObjRender_SetPosition(obj, 550, 80, 1);
		while(ObjRender_GetX(obj)>160){
			ObjRender_SetX(obj, ObjRender_GetX(obj) -20);
			yield;
		}
		loop(80){
			ObjRender_SetX(obj, ObjRender_GetX(obj) -1);
			yield;
		}
		loop(30){
			ObjRender_SetX(obj, ObjRender_GetX(obj) -20);
			yield;
		}
		Obj_Delete(obj);
	}
}


task SpellWarn(numBoss){
	let objText = ObjText_Create();
	if(numBoss==2){ObjText_SetText(objText, "WARNING!![r]SPELL INCOMING!");}
	else{ObjText_SetText(objText, "PREPARE TO CRY!");}
	ObjText_SetFontSize(objText, 40);
	ObjText_SetFontType(objText, "MS Gothic");
	ObjText_SetFontBold(objText, true);
	ObjText_SetFontColorTop(objText, 180, 0, 0);
	ObjText_SetFontColorBottom(objText, 80, 0, 120);
	ObjText_SetHorizontalAlignment(objText, ALIGNMENT_CENTER);
	
	ObjText_SetMaxWidth(objText, 360);
	Obj_SetRenderPriorityI(objText, 75);
	ObjRender_SetPosition(objText, -320, 20, 1);
	loop(16){
	    ObjRender_SetX(objText, ObjRender_GetX(objText) +20);
		yield;
	}
	let n=40; if(numBoss==3){n=45;}
	loop(n){
	    ObjRender_SetX(objText, ObjRender_GetX(objText) +1);
		wait(2);
	}
	loop(30){
	    ObjRender_SetX(objText, ObjRender_GetX(objText) +20);
		yield;
	}
	Obj_Delete(objText);
}


task SpellName(name, objBoss){
	let objText = ObjText_Create();
	ObjText_SetText(objText, name);
	ObjText_SetFontSize(objText, 20);
	ObjText_SetFontType(objText, "Arial");
	ObjText_SetFontColorTop(objText, 250, 0, 0);
	ObjText_SetFontColorBottom(objText, 250, 0, 0);
	ObjText_SetFontBold(objText, true);
    ObjText_SetHorizontalAlignment(objText, ALIGNMENT_CENTER);
	ObjText_SetMaxWidth(objText, 300);
	Obj_SetRenderPriorityI(objText, 78);
	
	ObjRender_SetPosition(objText, 3*384/4- 170, 300, 1);
	wait(120);
	ObjText_SetMaxWidth(objText, 260);
	ObjText_SetHorizontalAlignment(objText, ALIGNMENT_RIGHT);
	ObjRender_SetPosition(objText, 175, 15, 1);
	while(ObjEnemy_GetInfo(objBoss, INFO_LIFE)>0){yield;}
	Obj_Delete(objText);
}


task failBonus{
    let objText = ObjText_Create();
	ObjText_SetText(objText, "SPELL BONUS[r]FAILED..");
	Obj_SetRenderPriorityI(objText, 80);
	ObjText_SetFontSize(objText, 40);
	ObjText_SetFontType(objText, "MS Gothic");
	ObjText_SetFontBold(objText, true);
	ObjText_SetFontColorTop(objText, 200, 0, 0);
	ObjText_SetFontColorBottom(objText, 100, 0, 150);
	ObjText_SetHorizontalAlignment(objText, ALIGNMENT_CENTER);
	
	ObjText_SetMaxWidth(objText, 360);
	Obj_SetRenderPriorityI(objText, 80);
	ObjRender_SetPosition(objText, 222-180, 20, 1);
	wait(120);
	Obj_Delete(objText);
}












// Animation.


task animNon(objBoss, num){
    alternative(num)
	case(1){
		let img = GetCurrentScriptDirectory() ~ "../img/non.png";
		let i=0;
		ObjPrim_SetTexture(objBoss, img);
		while(ObjEnemy_GetInfo(objBoss, INFO_LIFE)>0){
			if(i%2==0){ObjSprite2D_SetSourceRect(objBoss, 96, 245, 396, 800); ObjSprite2D_SetDestRect(objBoss, -35, -75, 45, 75);}
			else{ObjSprite2D_SetSourceRect(objBoss, 65+512, 260, 415+512, 790); ObjSprite2D_SetDestRect(objBoss, -45, -75, 55, 75);}
			i++;
			wait(30);
		}
	}
	case(2){
	    let img = GetCurrentScriptDirectory() ~ "../img/NonSpellDave.png";
		let i=0;
		ObjPrim_SetTexture(objBoss, img);
		while(ObjEnemy_GetInfo(objBoss, INFO_LIFE)>0){
			if(i%2 ==0){
				ObjSprite2D_SetSourceRect(objBoss, 78, 85, 270, 385); 
				ObjSprite2D_SetDestRect(objBoss, -47, -70, 47, 70);
			}
			else{
				ObjSprite2D_SetSourceRect(objBoss, 78, 580, 270, 890); 
				ObjSprite2D_SetDestRect(objBoss, -47, -70, 47, 70);
			}
			i++;
			wait(30);
		}
	}
}


task animSpell(objBoss, num){
    alternative(num)
	case(1){
		let img = GetCurrentScriptDirectory() ~ "../img/spell.png";
		ObjPrim_SetTexture(objBoss, img);
		let i=0;
		while(ObjEnemy_GetInfo(objBoss, INFO_LIFE)>0){
			if(i%2 ==0){ObjSprite2D_SetSourceRect(objBoss, 55, 245, 390, 795); ObjSprite2D_SetDestRect(objBoss, -45, -75, 45, 75);}
			else{ObjSprite2D_SetSourceRect(objBoss, 60+512, 270, 445+512, 800); ObjSprite2D_SetDestRect(objBoss, -54, -75, 54, 75);}
			i++;
			wait(30);
		}
	}
	case(2){
	    let img = GetCurrentScriptDirectory() ~ "../img/SpellDave.png";
		ObjPrim_SetTexture(objBoss, img);
		let i=0;
		while(ObjEnemy_GetInfo(objBoss, INFO_LIFE)>0){
			if(i%2 ==0){
				ObjSprite2D_SetSourceRect(objBoss, 100, 60, 290, 380); 
				ObjSprite2D_SetDestRect(objBoss, -47, -70, 47, 70);
			}
			else{
				ObjSprite2D_SetSourceRect(objBoss, 50, 520, 310, 980); 
				ObjSprite2D_SetDestRect(objBoss, -85, -95, 40, 80);
			}
			i++;
			wait(30);
		}
	}
}











// Finalise.

task TFinalizeNon(scene, objBoss) {
    while(ObjEnemy_GetInfo(objBoss, INFO_LIFE) > 0){yield;}
	ObjSound_Play(LoadEffect(GetCurrentScriptDirectory~ "./../SFX/ATTACK3.WAV", 100));
    DeleteShotAll(TYPE_ALL, TYPE_FADE);
	if(ObjEnemyBossScene_GetInfo(scene, INFO_PLAYER_SHOOTDOWN_COUNT)      
      +ObjEnemyBossScene_GetInfo(scene, INFO_PLAYER_SPELL_COUNT) == 0 && ObjEnemyBossScene_GetInfo(scene, INFO_TIMER)>0){
	    ObjSound_Play(LoadEffect(GetCurrentScriptDirectory ~"./../SFX/BONUS2.WAV", 100));
		SetPlayerSpell(GetPlayerSpell+1);
    }
	wait(30);
	Obj_Delete(objBoss);
    SetAutoDeleteObject(true);
    CloseScript(GetOwnScriptID());
    return;
}

task TFinalize(objBoss) {   
    while(ObjEnemy_GetInfo(objBoss, INFO_LIFE) > 0){yield;}
	Obj_Delete(objBoss);
    CloseScript(GetOwnScriptID());
    return;
}



task TFinalizeSpell(scene, objBoss, destroy) {
    while(ObjEnemy_GetInfo(objBoss, INFO_LIFE) > 0){yield;}
	if(destroy){
	    StartSlow(TARGET_ALL, 30);
		let a = LoadEffect(GetCurrentScriptDirectory~ "./../SFX/ATTACK3.WAV", 70);
		loop(4){ObjSound_Play(a); wait(15);}
		StopSlow(TARGET_ALL);
	}
    Obj_Delete(objBoss);
    DeleteShotAll(TYPE_ALL, TYPE_IMMEDIATE);
	if(ObjEnemyBossScene_GetInfo(scene, INFO_PLAYER_SHOOTDOWN_COUNT)      
      +ObjEnemyBossScene_GetInfo(scene, INFO_PLAYER_SPELL_COUNT) == 0){
	    if(ObjEnemyBossScene_GetInfo(scene, INFO_TIMER)>0){
			ObjSound_Play(LoadEffect(GetCurrentScriptDirectory ~"./../SFX/BONUS3.WAV", 100));
			SetPlayerLife(GetPlayerLife()+1);
		}
		else{
		    ObjSound_Play(LoadEffect(GetCurrentScriptDirectory~ "./../SFX/ATTACK3.WAV", 80));
	        failBonus;
		}
    }   
	else{
	      ObjSound_Play(LoadEffect(GetCurrentScriptDirectory~ "./../SFX/ATTACK3.WAV", 80));
	      failBonus;
    }
	if(destroy){ObjSound_Play(LoadEffect(GetCurrentScriptDirectory~ "./../SFX/DEFEATED.WAV", 100));}
	wait(240);
    SetAutoDeleteObject(true);
    CloseScript(GetOwnScriptID());
    return;
}









// Sound.

function LoadEffect(sound, volume){
    let obj1 = ObjSound_Create();
	ObjSound_Load(obj1, sound);
	ObjSound_SetRestartEnable(obj1, true);
	ObjSound_SetVolumeRate(obj1, volume);
	return(obj1);
}

task shotSound(obj, time, volume, delay, enemy, delete, limit, scene, timeLimit){
    ObjSound_SetVolumeRate(obj, volume);
	ObjSound_SetRestartEnable(obj, true);
	if(time<0){
	    if(delete){
			while(!Obj_IsDeleted(enemy) && ObjEnemyBossScene_GetInfo(scene, INFO_TIMER)>timeLimit){
				ObjSound_Play(obj);
				wait(delay);
			}
		}
		else{
		    while(ObjEnemy_GetInfo(enemy, INFO_LIFE) > limit && ObjEnemyBossScene_GetInfo(scene, INFO_TIMER)>timeLimit){
				ObjSound_Play(obj);
				wait(delay);
			}
		}
	}
	else{
		let t=0;
		while(t<time){
			if(ObjEnemy_GetInfo(enemy, INFO_LIFE) <= limit || ObjEnemyBossScene_GetInfo(scene, INFO_TIMER)<= timeLimit ){return;}
			ObjSound_Play(obj);
			wait(delay);
			t+=delay;
		}
	}
}	


task LaserSound(time, las, objBoss, hp){
    let obj = LoadEffect(GetCurrentScriptDirectory~"./../SFX/LaserSmall.WAV", 100); let l = length(las);
	ObjSound_Play(obj); let t=0; let del;
	while(t<time-30 && ObjEnemy_GetInfo(objBoss, INFO_LIFE)>0){
	    if(ObjEnemy_GetInfo(objBoss, INFO_LIFE)<=hp){ObjSound_Stop(obj); return;}
	    del=[];
		ascent(i in 0.. l){del = del~ [Obj_IsDeleted(las[i])];}
		if(arrayAllTrue(del)){
		    ascent(i in 0..30) {
				ObjSound_SetVolumeRate(obj, 90- 3 *i);
				yield;
			}
			ObjSound_Stop(obj);
		}
		yield; t++;
	}
	ascent(i in 0..30) {
		ObjSound_SetVolumeRate(obj, 90- 3 *i);
		yield;
	}
	ObjSound_Stop(obj);	
}


function arrayAllTrue(arr){
    let ans = true;
    ascent(i in 0.. length(arr)){
	    if(arr[i]==false){ ans = false;}
	}
	return ans;
}









// Useful shot functions.


/* Useful for making helices. Speed is in frames/ second, but that does NOT mean that the helix will move at that speed
   (only the bullets will). Radius half the width of the helix, delay is the delay between bullets spawning. */

task helix(x, y, speed, angle, radius, gapLength, numBul, delay, topGraphic, bottomGraphic, enemy, limit){
    if(numBul<0){
	    while(ObjEnemy_GetInfo(enemy, INFO_LIFE)>limit){
		    if(topGraphic>0){
			    if(ObjEnemy_GetInfo(enemy, INFO_LIFE)<=limit){return;}
				let obj1 = CreateShotA1(x,y, 0,0, topGraphic, 0);
				sinePattern(obj1, x, y, speed, angle, radius, gapLength, 1);
		    }
			if(bottomGraphic>0){
			    if(ObjEnemy_GetInfo(enemy, INFO_LIFE)<=limit){return;}
				let obj2= CreateShotA1(x,y, 0,0, bottomGraphic, 0);
				sinePattern(obj2, x, y, speed, angle, radius, gapLength, -1);
			}
			wait(delay);
		}
	}
	loop(numBul){
	    if(topGraphic>0){
		    if(ObjEnemy_GetInfo(enemy, INFO_LIFE)<=limit){return;}
			let obj1 = CreateShotA1(x,y, 0,0, topGraphic, 0);
			sinePattern(obj1, x, y, speed, angle, radius, gapLength, 1);
		}
		if(bottomGraphic>0){
		    if(ObjEnemy_GetInfo(enemy, INFO_LIFE)<=limit){return;}
			let obj2= CreateShotA1(x,y, 0,0, bottomGraphic, 0);
			sinePattern(obj2, x, y, speed, angle, radius, gapLength, -1);
		}
		wait(delay);
	}
}

task sinePattern(obj, xini, yini, speed, angle, radius, gapLength, dir){
    let x; let y; let t = 1; let c = cos(angle); let s = sin(angle);
	while(!Obj_IsDeleted(obj)){
	    x = xini+ gapLength/180 * t *c -radius* dir* sin(t)*s;
		y = yini+ gapLength/180 * t *s +radius* dir* sin(t)*c;
		ObjMove_SetDestAtFrame(obj, x,y,1);
		t+=speed; yield;
	}
}





// Useful for making wavy patterns.

task wavy(obj, wvel, pause){
    let t=0; ObjMove_SetAngularVelocity(obj, wvel);
    while(!Obj_IsDeleted(obj)){
	    while(t<pause){t++; yield;}
		wvel*=-1;
	    ObjMove_SetAngularVelocity(obj, wvel);
		yield; t=0;
	}
}

// Self-explanatory.

task wavySpiral(numBranches, duration, dir, delayBetweenBul, speed, wvel, delayBetweenWvel, col1,col2, enemy, limit){
	let t=0; let ang; let col= floor(rand(col1, col2));
	while(t<duration){
		ang = rand(0,360);
		ascent(i in 0..numBranches){
		    if(ObjEnemy_GetInfo(enemy, INFO_LIFE)<=limit){return;}
			let obj = CreateShotA1(ObjMove_GetX(enemy), ObjMove_GetY(enemy), speed, ang+360/numBranches *i, col, 0);
			wavy(obj, wvel, delayBetweenWvel);
		}
		wait(delayBetweenBul); t+=delayBetweenBul;
	}
}






// Movement functions/ functions relating to movement.


task move(xmin, xmax, ymin, ymax, iniDel, time, delay, objBoss, limit, scene, timeLimit){
    wait(iniDel);
    while(ObjEnemy_GetInfo(objBoss, INFO_LIFE) > limit && ObjEnemyBossScene_GetInfo(scene, INFO_TIMER)>timeLimit){
	    ObjMove_SetDestAtFrame(objBoss, rand(xmin, xmax), rand(ymin, ymax), time);
		wait(delay);
	}
}


task DelayLaser(x, y, ang, len, wid, graph, time){ 
	let objlaser = CreateStraightLaserA1(x, y, ang, len, wid, 1, graph, time);
	ObjStLaser_SetSource(objlaser, false);
	wait(time-1);
	Obj_Delete(objlaser);
}


function findAngRad(xdest, x, ydest, y){
    let rad = power((x-xdest)^2 +(y-ydest)^2, 1/2); let angle = atan((ydest-y)/(xdest-x));
	if(xdest<x){angle = angle+180;}
	return [angle, rad];
}


function findAngFal(obj){
    while(ObjMove_GetSpeed(obj)>0){yield;}
	let ang = ObjMove_GetAngle(obj);
	if(ang<=270){ang = 90+ (3*180-2*ang)/4;}
	else{ang= 360-ang;}
}

	
task aimPlayer(obj, iniDel, period){
    wait(iniDel);
	loop(period){ObjMove_SetAngle(obj, GetAngleToPlayer(obj)); yield;}
}



task grazeReward(num, enemy, limit){
    let graze = GetGraze; let grazen;
    while(ObjEnemy_GetInfo(enemy, INFO_LIFE)>limit){
	    grazen = GetGraze;
		if(grazen> graze && grazen%num==0){
	        graze= grazen;
		    DeleteShotAll(TYPE_SHOT, TYPE_FADE);
			loop(59){DeleteShotAll(TYPE_SHOT, TYPE_FADE); yield;}
		}
		yield; 
	}
}